Cache images from frame to frame.
Images are cached for one frame; if they are not used the next frame, they're freed.
This commit is contained in:
parent
f62f85354f
commit
bb89f52d39
|
@ -53,6 +53,10 @@ pub struct Image {
|
||||||
is_opaque: bool,
|
is_opaque: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unique identifier for an image.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct ImageHash(pub u64);
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct PatternFlags: u8 {
|
pub struct PatternFlags: u8 {
|
||||||
const REPEAT_X = 0x01;
|
const REPEAT_X = 0x01;
|
||||||
|
@ -185,6 +189,13 @@ impl Image {
|
||||||
pub fn is_opaque(&self) -> bool {
|
pub fn is_opaque(&self) -> bool {
|
||||||
self.is_opaque
|
self.is_opaque
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_hash(&self) -> ImageHash {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
self.hash(&mut hasher);
|
||||||
|
ImageHash(hasher.finish())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PatternSource {
|
impl PatternSource {
|
||||||
|
|
|
@ -1541,6 +1541,9 @@ fn main() {
|
||||||
let mut cpu_graph = PerfGraph::new(GraphStyle::MS, "CPU Time");
|
let mut cpu_graph = PerfGraph::new(GraphStyle::MS, "CPU Time");
|
||||||
let mut gpu_graph = PerfGraph::new(GraphStyle::MS, "GPU Time");
|
let mut gpu_graph = PerfGraph::new(GraphStyle::MS, "GPU Time");
|
||||||
|
|
||||||
|
// Initialize scene proxy.
|
||||||
|
let mut scene = SceneProxy::new(renderer.mode().level, RayonExecutor);
|
||||||
|
|
||||||
// Enter the main loop.
|
// Enter the main loop.
|
||||||
let mut exit = false;
|
let mut exit = false;
|
||||||
while !exit {
|
while !exit {
|
||||||
|
@ -1569,9 +1572,7 @@ fn main() {
|
||||||
|
|
||||||
// Render the canvas to screen.
|
// Render the canvas to screen.
|
||||||
let canvas = context.into_canvas();
|
let canvas = context.into_canvas();
|
||||||
let mut scene = SceneProxy::from_scene(canvas.into_scene(),
|
scene.replace_scene(canvas.into_scene());
|
||||||
renderer.mode().level,
|
|
||||||
RayonExecutor);
|
|
||||||
scene.build_and_render(&mut renderer, BuildOptions::default());
|
scene.build_and_render(&mut renderer, BuildOptions::default());
|
||||||
|
|
||||||
// Present the rendered canvas via `surfman`.
|
// Present the rendered canvas via `surfman`.
|
||||||
|
|
|
@ -18,7 +18,7 @@ const ATLAS_TEXTURE_LENGTH: u32 = 1024;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TextureAllocator {
|
pub struct TextureAllocator {
|
||||||
pages: Vec<TexturePage>,
|
pages: Vec<Option<TexturePage>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -71,22 +71,34 @@ impl TextureAllocator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to add to each atlas.
|
// Try to add to each atlas.
|
||||||
|
let mut first_free_page_index = self.pages.len();
|
||||||
for (page_index, page) in self.pages.iter_mut().enumerate() {
|
for (page_index, page) in self.pages.iter_mut().enumerate() {
|
||||||
|
match *page {
|
||||||
|
Some(ref mut page) => {
|
||||||
match page.allocator {
|
match page.allocator {
|
||||||
TexturePageAllocator::Image { .. } => {}
|
TexturePageAllocator::Image { .. } => {}
|
||||||
TexturePageAllocator::Atlas(ref mut allocator) => {
|
TexturePageAllocator::Atlas(ref mut allocator) => {
|
||||||
if let Some(rect) = allocator.allocate(requested_size) {
|
if let Some(rect) = allocator.allocate(requested_size) {
|
||||||
return TextureLocation { page: TexturePageId(page_index as u32), rect };
|
return TextureLocation {
|
||||||
|
page: TexturePageId(page_index as u32),
|
||||||
|
rect
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => first_free_page_index = first_free_page_index.min(page_index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add a new atlas.
|
// Add a new atlas.
|
||||||
let page = TexturePageId(self.pages.len() as u32);
|
let page = self.get_first_free_page_id();
|
||||||
let mut allocator = TextureAtlasAllocator::new();
|
let mut allocator = TextureAtlasAllocator::new();
|
||||||
let rect = allocator.allocate(requested_size).expect("Allocation failed!");
|
let rect = allocator.allocate(requested_size).expect("Allocation failed!");
|
||||||
self.pages.push(TexturePage {
|
while (page.0 as usize) >= self.pages.len() {
|
||||||
|
self.pages.push(None);
|
||||||
|
}
|
||||||
|
self.pages[page.0 as usize] = Some(TexturePage {
|
||||||
is_new: true,
|
is_new: true,
|
||||||
allocator: TexturePageAllocator::Atlas(allocator),
|
allocator: TexturePageAllocator::Atlas(allocator),
|
||||||
});
|
});
|
||||||
|
@ -94,17 +106,52 @@ impl TextureAllocator {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub 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 page = self.get_first_free_page_id();
|
||||||
let rect = RectI::new(Vector2I::default(), requested_size);
|
let rect = RectI::new(Vector2I::default(), requested_size);
|
||||||
self.pages.push(TexturePage {
|
while (page.0 as usize) >= self.pages.len() {
|
||||||
|
self.pages.push(None);
|
||||||
|
}
|
||||||
|
self.pages[page.0 as usize] = Some(TexturePage {
|
||||||
is_new: true,
|
is_new: true,
|
||||||
allocator: TexturePageAllocator::Image { size: rect.size() },
|
allocator: TexturePageAllocator::Image { size: rect.size() },
|
||||||
});
|
});
|
||||||
TextureLocation { page, rect }
|
TextureLocation { page, rect }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_first_free_page_id(&self) -> TexturePageId {
|
||||||
|
for (page_index, page) in self.pages.iter().enumerate() {
|
||||||
|
if page.is_none() {
|
||||||
|
return TexturePageId(page_index as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TexturePageId(self.pages.len() as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free(&mut self, location: TextureLocation) {
|
||||||
|
//println!("free({:?})", location);
|
||||||
|
match self.pages[location.page.0 as usize]
|
||||||
|
.as_mut()
|
||||||
|
.expect("Texture page is not allocated!")
|
||||||
|
.allocator {
|
||||||
|
TexturePageAllocator::Image { size } => {
|
||||||
|
debug_assert_eq!(location.rect, RectI::new(Vector2I::default(), size));
|
||||||
|
}
|
||||||
|
TexturePageAllocator::Atlas(ref mut atlas_allocator) => {
|
||||||
|
atlas_allocator.free(location.rect);
|
||||||
|
if !atlas_allocator.is_empty() {
|
||||||
|
// Keep the page around.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here, free the page.
|
||||||
|
// TODO(pcwalton): Actually tell the renderer to free this page!
|
||||||
|
self.pages[location.page.0 as usize] = None;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn page_size(&self, page_id: TexturePageId) -> Vector2I {
|
pub fn page_size(&self, page_id: TexturePageId) -> Vector2I {
|
||||||
match self.pages[page_id.0 as usize].allocator {
|
match self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").allocator {
|
||||||
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
|
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
|
||||||
TexturePageAllocator::Image { size, .. } => size,
|
TexturePageAllocator::Image { size, .. } => size,
|
||||||
}
|
}
|
||||||
|
@ -115,16 +162,23 @@ impl TextureAllocator {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn page_is_new(&self, page_id: TexturePageId) -> bool {
|
pub fn page_is_new(&self, page_id: TexturePageId) -> bool {
|
||||||
self.pages[page_id.0 as usize].is_new
|
self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").is_new
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mark_page_as_allocated(&mut self, page_id: TexturePageId) {
|
pub fn mark_all_pages_as_allocated(&mut self) {
|
||||||
self.pages[page_id.0 as usize].is_new = false;
|
for page in &mut self.pages {
|
||||||
|
if let Some(ref mut page) = *page {
|
||||||
|
page.is_new = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub fn page_ids(&self) -> TexturePageIter {
|
||||||
pub fn page_count(&self) -> u32 {
|
let mut first_index = 0;
|
||||||
self.pages.len() as u32
|
while first_index < self.pages.len() && self.pages[first_index].is_none() {
|
||||||
|
first_index += 1;
|
||||||
|
}
|
||||||
|
TexturePageIter { allocator: self, next_index: first_index }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +201,6 @@ impl TextureAtlasAllocator {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(dead_code)]
|
|
||||||
fn free(&mut self, rect: RectI) {
|
fn free(&mut self, rect: RectI) {
|
||||||
let requested_length = rect.width() as u32;
|
let requested_length = rect.width() as u32;
|
||||||
self.root.free(Vector2I::default(), self.size, rect.origin(), requested_length)
|
self.root.free(Vector2I::default(), self.size, rect.origin(), requested_length)
|
||||||
|
@ -285,6 +338,30 @@ impl TreeNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TexturePageIter<'a> {
|
||||||
|
allocator: &'a TextureAllocator,
|
||||||
|
next_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for TexturePageIter<'a> {
|
||||||
|
type Item = TexturePageId;
|
||||||
|
fn next(&mut self) -> Option<TexturePageId> {
|
||||||
|
let next_id = if self.next_index >= self.allocator.pages.len() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(TexturePageId(self.next_index as u32))
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
self.next_index += 1;
|
||||||
|
if self.next_index >= self.allocator.pages.len() ||
|
||||||
|
self.allocator.pages[self.next_index as usize].is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use pathfinder_geometry::vector::vec2i;
|
use pathfinder_geometry::vector::vec2i;
|
||||||
|
|
|
@ -175,7 +175,7 @@ impl<'a, 'b, 'c, 'd> SceneBuilder<'a, 'b, 'c, 'd> {
|
||||||
render_commands,
|
render_commands,
|
||||||
paint_metadata,
|
paint_metadata,
|
||||||
render_target_metadata: _,
|
render_target_metadata: _,
|
||||||
} = self.scene.build_paint_info(render_transform);
|
} = self.scene.build_paint_info(&mut self.sink.paint_texture_manager, render_transform);
|
||||||
for render_command in render_commands {
|
for render_command in render_commands {
|
||||||
self.sink.listener.send(render_command);
|
self.sink.listener.send(render_command);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,11 @@ use crate::allocator::{AllocationMode, TextureAllocator};
|
||||||
use crate::gpu_data::{ColorCombineMode, RenderCommand, TextureLocation, TextureMetadataEntry};
|
use crate::gpu_data::{ColorCombineMode, RenderCommand, TextureLocation, TextureMetadataEntry};
|
||||||
use crate::gpu_data::{TexturePageDescriptor, TexturePageId, TileBatchTexture};
|
use crate::gpu_data::{TexturePageDescriptor, TexturePageId, TileBatchTexture};
|
||||||
use crate::scene::{RenderTarget, SceneId};
|
use crate::scene::{RenderTarget, SceneId};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::{HashMap, HashSet};
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
use pathfinder_content::effects::{BlendMode, Filter, PatternFilter};
|
use pathfinder_content::effects::{BlendMode, Filter, PatternFilter};
|
||||||
use pathfinder_content::gradient::{Gradient, GradientGeometry, GradientWrap};
|
use pathfinder_content::gradient::{Gradient, GradientGeometry, GradientWrap};
|
||||||
use pathfinder_content::pattern::{Pattern, PatternSource};
|
use pathfinder_content::pattern::{ImageHash, Pattern, PatternSource};
|
||||||
use pathfinder_content::render_target::RenderTargetId;
|
use pathfinder_content::render_target::RenderTargetId;
|
||||||
use pathfinder_geometry::line_segment::LineSegment2F;
|
use pathfinder_geometry::line_segment::LineSegment2F;
|
||||||
use pathfinder_geometry::rect::{RectF, RectI};
|
use pathfinder_geometry::rect::{RectF, RectI};
|
||||||
|
@ -35,19 +35,19 @@ use std::sync::Arc;
|
||||||
// TODO(pcwalton): Choose this size dynamically!
|
// TODO(pcwalton): Choose this size dynamically!
|
||||||
const GRADIENT_TILE_LENGTH: u32 = 256;
|
const GRADIENT_TILE_LENGTH: u32 = 256;
|
||||||
|
|
||||||
|
// Stores all paints in a scene.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct Palette {
|
pub(crate) struct Palette {
|
||||||
pub(crate) paints: Vec<Paint>,
|
pub(crate) paints: Vec<Paint>,
|
||||||
render_targets: Vec<RenderTargetData>,
|
render_targets: Vec<RenderTarget>,
|
||||||
cache: HashMap<Paint, PaintId>,
|
cache: HashMap<Paint, PaintId>,
|
||||||
allocator: TextureAllocator,
|
|
||||||
scene_id: SceneId,
|
scene_id: SceneId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
// Caches texture images from scene to scene.
|
||||||
struct RenderTargetData {
|
pub(crate) struct PaintTextureManager {
|
||||||
render_target: RenderTarget,
|
allocator: TextureAllocator,
|
||||||
metadata: RenderTargetMetadata,
|
cached_images: HashMap<ImageHash, TextureLocation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines how a path is to be filled: with a solid color, gradient, or pattern.
|
/// Defines how a path is to be filled: with a solid color, gradient, or pattern.
|
||||||
|
@ -109,7 +109,6 @@ impl Palette {
|
||||||
paints: vec![],
|
paints: vec![],
|
||||||
render_targets: vec![],
|
render_targets: vec![],
|
||||||
cache: HashMap::new(),
|
cache: HashMap::new(),
|
||||||
allocator: TextureAllocator::new(),
|
|
||||||
scene_id,
|
scene_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,25 +391,79 @@ impl Palette {
|
||||||
|
|
||||||
pub(crate) fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId {
|
pub(crate) fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId {
|
||||||
let id = self.render_targets.len() as u32;
|
let id = self.render_targets.len() as u32;
|
||||||
|
self.render_targets.push(render_target);
|
||||||
let metadata = RenderTargetMetadata {
|
|
||||||
location: self.allocator.allocate_image(render_target.size()),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.render_targets.push(RenderTargetData { render_target, metadata });
|
|
||||||
RenderTargetId { scene: self.scene_id.0, render_target: id }
|
RenderTargetId { scene: self.scene_id.0, render_target: id }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo {
|
pub(crate) fn build_paint_info(&mut self,
|
||||||
let mut paint_metadata = vec![];
|
texture_manager: &mut PaintTextureManager,
|
||||||
|
render_transform: Transform2F)
|
||||||
|
-> PaintInfo {
|
||||||
|
// Assign render target locations.
|
||||||
|
let mut transient_paint_locations = vec![];
|
||||||
|
let render_target_metadata =
|
||||||
|
self.assign_render_target_locations(texture_manager, &mut transient_paint_locations);
|
||||||
|
|
||||||
// Assign paint locations.
|
// Assign paint locations.
|
||||||
|
let PaintLocationsInfo {
|
||||||
|
mut paint_metadata,
|
||||||
|
gradient_tile_builder,
|
||||||
|
image_texel_info,
|
||||||
|
used_image_hashes,
|
||||||
|
} = self.assign_paint_locations(&render_target_metadata,
|
||||||
|
texture_manager,
|
||||||
|
&mut transient_paint_locations);
|
||||||
|
|
||||||
|
// Calculate texture transforms.
|
||||||
|
self.calculate_texture_transforms(&mut paint_metadata, texture_manager, render_transform);
|
||||||
|
|
||||||
|
// Create texture metadata.
|
||||||
|
let texture_metadata = self.create_texture_metadata(&paint_metadata);
|
||||||
|
let mut render_commands = vec![RenderCommand::UploadTextureMetadata(texture_metadata)];
|
||||||
|
|
||||||
|
// Allocate textures.
|
||||||
|
self.allocate_textures(&mut render_commands, texture_manager);
|
||||||
|
|
||||||
|
// Create render commands.
|
||||||
|
self.create_render_commands(&mut render_commands,
|
||||||
|
&render_target_metadata,
|
||||||
|
gradient_tile_builder,
|
||||||
|
image_texel_info);
|
||||||
|
|
||||||
|
// Free transient locations and unused images, now that they're no longer needed.
|
||||||
|
self.free_transient_locations(texture_manager, transient_paint_locations);
|
||||||
|
self.free_unused_images(texture_manager, used_image_hashes);
|
||||||
|
|
||||||
|
PaintInfo { render_commands, paint_metadata, render_target_metadata }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_render_target_locations(&self,
|
||||||
|
texture_manager: &mut PaintTextureManager,
|
||||||
|
transient_paint_locations: &mut Vec<TextureLocation>)
|
||||||
|
-> Vec<RenderTargetMetadata> {
|
||||||
|
let mut render_target_metadata = vec![];
|
||||||
|
for render_target in &self.render_targets {
|
||||||
|
let location = texture_manager.allocator.allocate_image(render_target.size());
|
||||||
|
render_target_metadata.push(RenderTargetMetadata { location });
|
||||||
|
transient_paint_locations.push(location);
|
||||||
|
}
|
||||||
|
render_target_metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_paint_locations(&self,
|
||||||
|
render_target_metadata: &[RenderTargetMetadata],
|
||||||
|
texture_manager: &mut PaintTextureManager,
|
||||||
|
transient_paint_locations: &mut Vec<TextureLocation>)
|
||||||
|
-> PaintLocationsInfo {
|
||||||
|
let mut paint_metadata = vec![];
|
||||||
let mut gradient_tile_builder = GradientTileBuilder::new();
|
let mut gradient_tile_builder = GradientTileBuilder::new();
|
||||||
let mut image_texel_info = vec![];
|
let mut image_texel_info = vec![];
|
||||||
|
let mut used_image_hashes = HashSet::new();
|
||||||
for paint in &self.paints {
|
for paint in &self.paints {
|
||||||
let allocator = &mut self.allocator;
|
let allocator = &mut texture_manager.allocator;
|
||||||
let render_targets = &self.render_targets;
|
let color_texture_metadata = match paint.overlay {
|
||||||
let color_texture_metadata = paint.overlay.as_ref().map(|overlay| {
|
None => None,
|
||||||
|
Some(ref overlay) => {
|
||||||
match overlay.contents {
|
match overlay.contents {
|
||||||
PaintContents::Gradient(ref gradient) => {
|
PaintContents::Gradient(ref gradient) => {
|
||||||
let mut sampling_flags = TextureSamplingFlags::empty();
|
let mut sampling_flags = TextureSamplingFlags::empty();
|
||||||
|
@ -421,9 +474,13 @@ impl Palette {
|
||||||
GradientWrap::Clamp => {}
|
GradientWrap::Clamp => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(pcwalton): The gradient size might not be big enough. Detect this.
|
// FIXME(pcwalton): The gradient size might not be big enough. Detect
|
||||||
let location = gradient_tile_builder.allocate(allocator, gradient);
|
// this.
|
||||||
PaintColorTextureMetadata {
|
let location =
|
||||||
|
gradient_tile_builder.allocate(allocator,
|
||||||
|
transient_paint_locations,
|
||||||
|
gradient);
|
||||||
|
Some(PaintColorTextureMetadata {
|
||||||
location,
|
location,
|
||||||
page_scale: allocator.page_scale(location.page),
|
page_scale: allocator.page_scale(location.page),
|
||||||
sampling_flags,
|
sampling_flags,
|
||||||
|
@ -435,20 +492,35 @@ impl Palette {
|
||||||
},
|
},
|
||||||
transform: Transform2F::default(),
|
transform: Transform2F::default(),
|
||||||
composite_op: overlay.composite_op(),
|
composite_op: overlay.composite_op(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
PaintContents::Pattern(ref pattern) => {
|
PaintContents::Pattern(ref pattern) => {
|
||||||
let location;
|
let location;
|
||||||
match *pattern.source() {
|
match *pattern.source() {
|
||||||
PatternSource::RenderTarget { id: render_target_id, .. } => {
|
PatternSource::RenderTarget { id: render_target_id, .. } => {
|
||||||
let index = render_target_id.render_target as usize;
|
let index = render_target_id.render_target as usize;
|
||||||
location = render_targets[index].metadata.location;
|
location = render_target_metadata[index].location;
|
||||||
}
|
}
|
||||||
PatternSource::Image(ref image) => {
|
PatternSource::Image(ref image) => {
|
||||||
// TODO(pcwalton): We should be able to use tile cleverness to
|
// TODO(pcwalton): We should be able to use tile cleverness to
|
||||||
// repeat inside the atlas in some cases.
|
// repeat inside the atlas in some cases.
|
||||||
|
let image_hash = image.get_hash();
|
||||||
|
//println!("image hash: {:?}", image_hash);
|
||||||
|
match texture_manager.cached_images.get(&image_hash) {
|
||||||
|
Some(cached_location) => {
|
||||||
|
//println!("... cache hit: {:?}", cached_location);
|
||||||
|
location = *cached_location;
|
||||||
|
used_image_hashes.insert(image_hash);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
//println!("... cache MISS");
|
||||||
let allocation_mode = AllocationMode::OwnPage;
|
let allocation_mode = AllocationMode::OwnPage;
|
||||||
location = allocator.allocate(image.size(), allocation_mode);
|
location = allocator.allocate(image.size(),
|
||||||
|
allocation_mode);
|
||||||
|
texture_manager.cached_images.insert(image_hash,
|
||||||
|
location);
|
||||||
|
}
|
||||||
|
}
|
||||||
image_texel_info.push(ImageTexelInfo {
|
image_texel_info.push(ImageTexelInfo {
|
||||||
location,
|
location,
|
||||||
texels: (*image.pixels()).clone(),
|
texels: (*image.pixels()).clone(),
|
||||||
|
@ -473,17 +545,18 @@ impl Palette {
|
||||||
Some(pattern_filter) => PaintFilter::PatternFilter(pattern_filter),
|
Some(pattern_filter) => PaintFilter::PatternFilter(pattern_filter),
|
||||||
};
|
};
|
||||||
|
|
||||||
PaintColorTextureMetadata {
|
Some(PaintColorTextureMetadata {
|
||||||
location,
|
location,
|
||||||
page_scale: allocator.page_scale(location.page),
|
page_scale: allocator.page_scale(location.page),
|
||||||
sampling_flags,
|
sampling_flags,
|
||||||
filter,
|
filter,
|
||||||
transform: Transform2F::default(),
|
transform: Transform2F::default(),
|
||||||
composite_op: overlay.composite_op(),
|
composite_op: overlay.composite_op(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
paint_metadata.push(PaintMetadata {
|
paint_metadata.push(PaintMetadata {
|
||||||
color_texture_metadata,
|
color_texture_metadata,
|
||||||
|
@ -494,14 +567,26 @@ impl Palette {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate texture transforms.
|
PaintLocationsInfo {
|
||||||
|
paint_metadata,
|
||||||
|
gradient_tile_builder,
|
||||||
|
image_texel_info,
|
||||||
|
used_image_hashes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_texture_transforms(&self,
|
||||||
|
paint_metadata: &mut [PaintMetadata],
|
||||||
|
texture_manager: &mut PaintTextureManager,
|
||||||
|
render_transform: Transform2F) {
|
||||||
for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter_mut()) {
|
for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter_mut()) {
|
||||||
let mut color_texture_metadata = match metadata.color_texture_metadata {
|
let mut color_texture_metadata = match metadata.color_texture_metadata {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(ref mut color_texture_metadata) => color_texture_metadata,
|
Some(ref mut color_texture_metadata) => color_texture_metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
let texture_scale = self.allocator.page_scale(color_texture_metadata.location.page);
|
let texture_scale = texture_manager.allocator
|
||||||
|
.page_scale(color_texture_metadata.location.page);
|
||||||
let texture_rect = color_texture_metadata.location.rect;
|
let texture_rect = color_texture_metadata.location.rect;
|
||||||
color_texture_metadata.transform = match paint.overlay
|
color_texture_metadata.transform = match paint.overlay
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -544,9 +629,11 @@ impl Palette {
|
||||||
};
|
};
|
||||||
color_texture_metadata.transform *= render_transform;
|
color_texture_metadata.transform *= render_transform;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create texture metadata.
|
fn create_texture_metadata(&self, paint_metadata: &[PaintMetadata])
|
||||||
let texture_metadata = paint_metadata.iter().map(|paint_metadata| {
|
-> Vec<TextureMetadataEntry> {
|
||||||
|
paint_metadata.iter().map(|paint_metadata| {
|
||||||
TextureMetadataEntry {
|
TextureMetadataEntry {
|
||||||
color_0_transform: match paint_metadata.color_texture_metadata {
|
color_0_transform: match paint_metadata.color_texture_metadata {
|
||||||
None => Transform2F::default(),
|
None => Transform2F::default(),
|
||||||
|
@ -561,29 +648,28 @@ impl Palette {
|
||||||
filter: paint_metadata.filter(),
|
filter: paint_metadata.filter(),
|
||||||
blend_mode: paint_metadata.blend_mode,
|
blend_mode: paint_metadata.blend_mode,
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect()
|
||||||
let mut render_commands = vec![RenderCommand::UploadTextureMetadata(texture_metadata)];
|
}
|
||||||
|
|
||||||
// Allocate textures.
|
fn allocate_textures(&self,
|
||||||
let mut texture_page_descriptors = vec![];
|
render_commands: &mut Vec<RenderCommand>,
|
||||||
for page_index in 0..self.allocator.page_count() {
|
texture_manager: &mut PaintTextureManager) {
|
||||||
let page_id = TexturePageId(page_index);
|
for page_id in texture_manager.allocator.page_ids() {
|
||||||
let page_size = self.allocator.page_size(page_id);
|
let page_size = texture_manager.allocator.page_size(page_id);
|
||||||
let descriptor = TexturePageDescriptor { size: page_size };
|
let descriptor = TexturePageDescriptor { size: page_size };
|
||||||
texture_page_descriptors.push(descriptor);
|
|
||||||
|
|
||||||
if self.allocator.page_is_new(page_id) {
|
if texture_manager.allocator.page_is_new(page_id) {
|
||||||
render_commands.push(RenderCommand::AllocateTexturePage { page_id, descriptor });
|
render_commands.push(RenderCommand::AllocateTexturePage { page_id, descriptor });
|
||||||
self.allocator.mark_page_as_allocated(page_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
texture_manager.allocator.mark_all_pages_as_allocated();
|
||||||
|
}
|
||||||
|
|
||||||
// Gather up render target metadata.
|
fn create_render_commands(&self,
|
||||||
let render_target_metadata: Vec<_> = self.render_targets.iter().map(|render_target_data| {
|
render_commands: &mut Vec<RenderCommand>,
|
||||||
render_target_data.metadata
|
render_target_metadata: &[RenderTargetMetadata],
|
||||||
}).collect();
|
gradient_tile_builder: GradientTileBuilder,
|
||||||
|
image_texel_info: Vec<ImageTexelInfo>) {
|
||||||
// Create render commands.
|
|
||||||
for (index, metadata) in render_target_metadata.iter().enumerate() {
|
for (index, metadata) in render_target_metadata.iter().enumerate() {
|
||||||
let id = RenderTargetId { scene: self.scene_id.0, render_target: index as u32 };
|
let id = RenderTargetId { scene: self.scene_id.0, render_target: index as u32 };
|
||||||
render_commands.push(RenderCommand::DeclareRenderTarget {
|
render_commands.push(RenderCommand::DeclareRenderTarget {
|
||||||
|
@ -591,15 +677,36 @@ impl Palette {
|
||||||
location: metadata.location,
|
location: metadata.location,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
gradient_tile_builder.create_render_commands(&mut render_commands);
|
gradient_tile_builder.create_render_commands(render_commands);
|
||||||
for image_texel_info in image_texel_info {
|
for image_texel_info in image_texel_info {
|
||||||
render_commands.push(RenderCommand::UploadTexelData {
|
render_commands.push(RenderCommand::UploadTexelData {
|
||||||
texels: image_texel_info.texels,
|
texels: image_texel_info.texels,
|
||||||
location: image_texel_info.location,
|
location: image_texel_info.location,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PaintInfo { render_commands, paint_metadata, render_target_metadata }
|
fn free_transient_locations(&self,
|
||||||
|
texture_manager: &mut PaintTextureManager,
|
||||||
|
transient_paint_locations: Vec<TextureLocation>) {
|
||||||
|
for location in transient_paint_locations {
|
||||||
|
texture_manager.allocator.free(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees images that are cached but not used this frame.
|
||||||
|
fn free_unused_images(&self,
|
||||||
|
texture_manager: &mut PaintTextureManager,
|
||||||
|
used_image_hashes: HashSet<ImageHash>) {
|
||||||
|
let cached_images = &mut texture_manager.cached_images;
|
||||||
|
let allocator = &mut texture_manager.allocator;
|
||||||
|
cached_images.retain(|image_hash, location| {
|
||||||
|
let keep = used_image_hashes.contains(image_hash);
|
||||||
|
if !keep {
|
||||||
|
allocator.free(*location);
|
||||||
|
}
|
||||||
|
keep
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn append_palette(&mut self, palette: Palette) -> MergedPaletteInfo {
|
pub(crate) fn append_palette(&mut self, palette: Palette) -> MergedPaletteInfo {
|
||||||
|
@ -612,7 +719,7 @@ impl Palette {
|
||||||
scene: palette.scene_id.0,
|
scene: palette.scene_id.0,
|
||||||
render_target: old_render_target_index as u32,
|
render_target: old_render_target_index as u32,
|
||||||
};
|
};
|
||||||
let new_render_target_id = self.push_render_target(render_target.render_target);
|
let new_render_target_id = self.push_render_target(render_target);
|
||||||
render_target_mapping.insert(old_render_target_id, new_render_target_id);
|
render_target_mapping.insert(old_render_target_id, new_render_target_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,6 +757,15 @@ impl Palette {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PaintTextureManager {
|
||||||
|
pub(crate) fn new() -> PaintTextureManager {
|
||||||
|
PaintTextureManager {
|
||||||
|
allocator: TextureAllocator::new(),
|
||||||
|
cached_images: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct MergedPaletteInfo {
|
pub(crate) struct MergedPaletteInfo {
|
||||||
pub(crate) render_target_mapping: HashMap<RenderTargetId, RenderTargetId>,
|
pub(crate) render_target_mapping: HashMap<RenderTargetId, RenderTargetId>,
|
||||||
pub(crate) paint_mapping: HashMap<PaintId, PaintId>,
|
pub(crate) paint_mapping: HashMap<PaintId, PaintId>,
|
||||||
|
@ -702,15 +818,20 @@ impl GradientTileBuilder {
|
||||||
GradientTileBuilder { tiles: vec![] }
|
GradientTileBuilder { tiles: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate(&mut self, allocator: &mut TextureAllocator, gradient: &Gradient)
|
fn allocate(&mut self,
|
||||||
|
allocator: &mut TextureAllocator,
|
||||||
|
transient_paint_locations: &mut Vec<TextureLocation>,
|
||||||
|
gradient: &Gradient)
|
||||||
-> TextureLocation {
|
-> TextureLocation {
|
||||||
if self.tiles.is_empty() ||
|
if self.tiles.is_empty() ||
|
||||||
self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH {
|
self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH {
|
||||||
let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32);
|
let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32);
|
||||||
let area = size.x() as usize * size.y() as usize;
|
let area = size.x() as usize * size.y() as usize;
|
||||||
|
let page_location = allocator.allocate(size, AllocationMode::OwnPage);
|
||||||
|
transient_paint_locations.push(page_location);
|
||||||
self.tiles.push(GradientTile {
|
self.tiles.push(GradientTile {
|
||||||
texels: vec![ColorU::black(); area],
|
texels: vec![ColorU::black(); area],
|
||||||
page: allocator.allocate(size, AllocationMode::OwnPage).page,
|
page: page_location.page,
|
||||||
next_index: 0,
|
next_index: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -749,6 +870,13 @@ impl GradientTileBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PaintLocationsInfo {
|
||||||
|
paint_metadata: Vec<PaintMetadata>,
|
||||||
|
gradient_tile_builder: GradientTileBuilder,
|
||||||
|
image_texel_info: Vec<ImageTexelInfo>,
|
||||||
|
used_image_hashes: HashSet<ImageHash>,
|
||||||
|
}
|
||||||
|
|
||||||
struct ImageTexelInfo {
|
struct ImageTexelInfo {
|
||||||
location: TextureLocation,
|
location: TextureLocation,
|
||||||
texels: Arc<Vec<ColorU>>,
|
texels: Arc<Vec<ColorU>>,
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::gpu::renderer::Renderer;
|
||||||
use crate::gpu_data::RenderCommand;
|
use crate::gpu_data::RenderCommand;
|
||||||
use crate::options::{BuildOptions, PreparedBuildOptions};
|
use crate::options::{BuildOptions, PreparedBuildOptions};
|
||||||
use crate::options::{PreparedRenderTransform, RenderCommandListener};
|
use crate::options::{PreparedRenderTransform, RenderCommandListener};
|
||||||
use crate::paint::{MergedPaletteInfo, Paint, PaintId, PaintInfo, Palette};
|
use crate::paint::{MergedPaletteInfo, Paint, PaintId, PaintInfo, PaintTextureManager, Palette};
|
||||||
use pathfinder_content::effects::BlendMode;
|
use pathfinder_content::effects::BlendMode;
|
||||||
use pathfinder_content::fill::FillRule;
|
use pathfinder_content::fill::FillRule;
|
||||||
use pathfinder_content::outline::Outline;
|
use pathfinder_content::outline::Outline;
|
||||||
|
@ -178,8 +178,11 @@ impl Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo {
|
pub(crate) fn build_paint_info(&mut self,
|
||||||
self.palette.build_paint_info(render_transform)
|
texture_manager: &mut PaintTextureManager,
|
||||||
|
render_transform: Transform2F)
|
||||||
|
-> PaintInfo {
|
||||||
|
self.palette.build_paint_info(texture_manager, render_transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines a new paint, which specifies how paths are to be filled or stroked. Returns a paint
|
/// Defines a new paint, which specifies how paths are to be filled or stroked. Returns a paint
|
||||||
|
@ -382,6 +385,7 @@ pub struct SceneSink<'a> {
|
||||||
pub(crate) listener: RenderCommandListener<'a>,
|
pub(crate) listener: RenderCommandListener<'a>,
|
||||||
pub(crate) renderer_level: RendererLevel,
|
pub(crate) renderer_level: RendererLevel,
|
||||||
pub(crate) last_scene: Option<LastSceneInfo>,
|
pub(crate) last_scene: Option<LastSceneInfo>,
|
||||||
|
pub(crate) paint_texture_manager: PaintTextureManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct LastSceneInfo {
|
pub(crate) struct LastSceneInfo {
|
||||||
|
@ -423,7 +427,12 @@ impl<'a> SceneSink<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(listener: RenderCommandListener<'a>, renderer_level: RendererLevel)
|
pub fn new(listener: RenderCommandListener<'a>, renderer_level: RendererLevel)
|
||||||
-> SceneSink<'a> {
|
-> SceneSink<'a> {
|
||||||
SceneSink { listener, renderer_level, last_scene: None }
|
SceneSink {
|
||||||
|
listener,
|
||||||
|
renderer_level,
|
||||||
|
last_scene: None,
|
||||||
|
paint_texture_manager: PaintTextureManager::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue