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,
|
||||
}
|
||||
|
||||
/// Unique identifier for an image.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ImageHash(pub u64);
|
||||
|
||||
bitflags! {
|
||||
pub struct PatternFlags: u8 {
|
||||
const REPEAT_X = 0x01;
|
||||
|
@ -185,6 +189,13 @@ impl Image {
|
|||
pub fn is_opaque(&self) -> bool {
|
||||
self.is_opaque
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hash(&self) -> ImageHash {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
self.hash(&mut hasher);
|
||||
ImageHash(hasher.finish())
|
||||
}
|
||||
}
|
||||
|
||||
impl PatternSource {
|
||||
|
|
|
@ -1541,6 +1541,9 @@ fn main() {
|
|||
let mut cpu_graph = PerfGraph::new(GraphStyle::MS, "CPU 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.
|
||||
let mut exit = false;
|
||||
while !exit {
|
||||
|
@ -1569,9 +1572,7 @@ fn main() {
|
|||
|
||||
// Render the canvas to screen.
|
||||
let canvas = context.into_canvas();
|
||||
let mut scene = SceneProxy::from_scene(canvas.into_scene(),
|
||||
renderer.mode().level,
|
||||
RayonExecutor);
|
||||
scene.replace_scene(canvas.into_scene());
|
||||
scene.build_and_render(&mut renderer, BuildOptions::default());
|
||||
|
||||
// Present the rendered canvas via `surfman`.
|
||||
|
|
|
@ -18,7 +18,7 @@ const ATLAS_TEXTURE_LENGTH: u32 = 1024;
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TextureAllocator {
|
||||
pages: Vec<TexturePage>,
|
||||
pages: Vec<Option<TexturePage>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -71,22 +71,34 @@ impl TextureAllocator {
|
|||
}
|
||||
|
||||
// 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() {
|
||||
match *page {
|
||||
Some(ref mut page) => {
|
||||
match page.allocator {
|
||||
TexturePageAllocator::Image { .. } => {}
|
||||
TexturePageAllocator::Atlas(ref mut allocator) => {
|
||||
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.
|
||||
let page = TexturePageId(self.pages.len() as u32);
|
||||
let page = self.get_first_free_page_id();
|
||||
let mut allocator = TextureAtlasAllocator::new();
|
||||
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,
|
||||
allocator: TexturePageAllocator::Atlas(allocator),
|
||||
});
|
||||
|
@ -94,17 +106,52 @@ impl TextureAllocator {
|
|||
}
|
||||
|
||||
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);
|
||||
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,
|
||||
allocator: TexturePageAllocator::Image { size: rect.size() },
|
||||
});
|
||||
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 {
|
||||
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::Image { size, .. } => size,
|
||||
}
|
||||
|
@ -115,16 +162,23 @@ impl TextureAllocator {
|
|||
}
|
||||
|
||||
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) {
|
||||
self.pages[page_id.0 as usize].is_new = false;
|
||||
pub fn mark_all_pages_as_allocated(&mut self) {
|
||||
for page in &mut self.pages {
|
||||
if let Some(ref mut page) = *page {
|
||||
page.is_new = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn page_count(&self) -> u32 {
|
||||
self.pages.len() as u32
|
||||
pub fn page_ids(&self) -> TexturePageIter {
|
||||
let mut first_index = 0;
|
||||
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]
|
||||
#[allow(dead_code)]
|
||||
fn free(&mut self, rect: RectI) {
|
||||
let requested_length = rect.width() as u32;
|
||||
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)]
|
||||
mod test {
|
||||
use pathfinder_geometry::vector::vec2i;
|
||||
|
|
|
@ -175,7 +175,7 @@ impl<'a, 'b, 'c, 'd> SceneBuilder<'a, 'b, 'c, 'd> {
|
|||
render_commands,
|
||||
paint_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 {
|
||||
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::{TexturePageDescriptor, TexturePageId, TileBatchTexture};
|
||||
use crate::scene::{RenderTarget, SceneId};
|
||||
use hashbrown::HashMap;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use pathfinder_color::ColorU;
|
||||
use pathfinder_content::effects::{BlendMode, Filter, PatternFilter};
|
||||
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_geometry::line_segment::LineSegment2F;
|
||||
use pathfinder_geometry::rect::{RectF, RectI};
|
||||
|
@ -35,19 +35,19 @@ use std::sync::Arc;
|
|||
// TODO(pcwalton): Choose this size dynamically!
|
||||
const GRADIENT_TILE_LENGTH: u32 = 256;
|
||||
|
||||
// Stores all paints in a scene.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Palette {
|
||||
pub(crate) paints: Vec<Paint>,
|
||||
render_targets: Vec<RenderTargetData>,
|
||||
render_targets: Vec<RenderTarget>,
|
||||
cache: HashMap<Paint, PaintId>,
|
||||
allocator: TextureAllocator,
|
||||
scene_id: SceneId,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct RenderTargetData {
|
||||
render_target: RenderTarget,
|
||||
metadata: RenderTargetMetadata,
|
||||
// Caches texture images from scene to scene.
|
||||
pub(crate) struct PaintTextureManager {
|
||||
allocator: TextureAllocator,
|
||||
cached_images: HashMap<ImageHash, TextureLocation>,
|
||||
}
|
||||
|
||||
/// Defines how a path is to be filled: with a solid color, gradient, or pattern.
|
||||
|
@ -109,7 +109,6 @@ impl Palette {
|
|||
paints: vec![],
|
||||
render_targets: vec![],
|
||||
cache: HashMap::new(),
|
||||
allocator: TextureAllocator::new(),
|
||||
scene_id,
|
||||
}
|
||||
}
|
||||
|
@ -392,25 +391,79 @@ impl Palette {
|
|||
|
||||
pub(crate) fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId {
|
||||
let id = self.render_targets.len() as u32;
|
||||
|
||||
let metadata = RenderTargetMetadata {
|
||||
location: self.allocator.allocate_image(render_target.size()),
|
||||
};
|
||||
|
||||
self.render_targets.push(RenderTargetData { render_target, metadata });
|
||||
self.render_targets.push(render_target);
|
||||
RenderTargetId { scene: self.scene_id.0, render_target: id }
|
||||
}
|
||||
|
||||
pub(crate) fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo {
|
||||
let mut paint_metadata = vec![];
|
||||
pub(crate) fn build_paint_info(&mut self,
|
||||
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.
|
||||
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 image_texel_info = vec![];
|
||||
let mut used_image_hashes = HashSet::new();
|
||||
for paint in &self.paints {
|
||||
let allocator = &mut self.allocator;
|
||||
let render_targets = &self.render_targets;
|
||||
let color_texture_metadata = paint.overlay.as_ref().map(|overlay| {
|
||||
let allocator = &mut texture_manager.allocator;
|
||||
let color_texture_metadata = match paint.overlay {
|
||||
None => None,
|
||||
Some(ref overlay) => {
|
||||
match overlay.contents {
|
||||
PaintContents::Gradient(ref gradient) => {
|
||||
let mut sampling_flags = TextureSamplingFlags::empty();
|
||||
|
@ -421,9 +474,13 @@ impl Palette {
|
|||
GradientWrap::Clamp => {}
|
||||
}
|
||||
|
||||
// FIXME(pcwalton): The gradient size might not be big enough. Detect this.
|
||||
let location = gradient_tile_builder.allocate(allocator, gradient);
|
||||
PaintColorTextureMetadata {
|
||||
// FIXME(pcwalton): The gradient size might not be big enough. Detect
|
||||
// this.
|
||||
let location =
|
||||
gradient_tile_builder.allocate(allocator,
|
||||
transient_paint_locations,
|
||||
gradient);
|
||||
Some(PaintColorTextureMetadata {
|
||||
location,
|
||||
page_scale: allocator.page_scale(location.page),
|
||||
sampling_flags,
|
||||
|
@ -435,20 +492,35 @@ impl Palette {
|
|||
},
|
||||
transform: Transform2F::default(),
|
||||
composite_op: overlay.composite_op(),
|
||||
}
|
||||
})
|
||||
}
|
||||
PaintContents::Pattern(ref pattern) => {
|
||||
let location;
|
||||
match *pattern.source() {
|
||||
PatternSource::RenderTarget { id: render_target_id, .. } => {
|
||||
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) => {
|
||||
// TODO(pcwalton): We should be able to use tile cleverness to
|
||||
// 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;
|
||||
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 {
|
||||
location,
|
||||
texels: (*image.pixels()).clone(),
|
||||
|
@ -473,17 +545,18 @@ impl Palette {
|
|||
Some(pattern_filter) => PaintFilter::PatternFilter(pattern_filter),
|
||||
};
|
||||
|
||||
PaintColorTextureMetadata {
|
||||
Some(PaintColorTextureMetadata {
|
||||
location,
|
||||
page_scale: allocator.page_scale(location.page),
|
||||
sampling_flags,
|
||||
filter,
|
||||
transform: Transform2F::default(),
|
||||
composite_op: overlay.composite_op(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
paint_metadata.push(PaintMetadata {
|
||||
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()) {
|
||||
let mut color_texture_metadata = match metadata.color_texture_metadata {
|
||||
None => continue,
|
||||
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;
|
||||
color_texture_metadata.transform = match paint.overlay
|
||||
.as_ref()
|
||||
|
@ -544,9 +629,11 @@ impl Palette {
|
|||
};
|
||||
color_texture_metadata.transform *= render_transform;
|
||||
}
|
||||
}
|
||||
|
||||
// Create texture metadata.
|
||||
let texture_metadata = paint_metadata.iter().map(|paint_metadata| {
|
||||
fn create_texture_metadata(&self, paint_metadata: &[PaintMetadata])
|
||||
-> Vec<TextureMetadataEntry> {
|
||||
paint_metadata.iter().map(|paint_metadata| {
|
||||
TextureMetadataEntry {
|
||||
color_0_transform: match paint_metadata.color_texture_metadata {
|
||||
None => Transform2F::default(),
|
||||
|
@ -561,29 +648,28 @@ impl Palette {
|
|||
filter: paint_metadata.filter(),
|
||||
blend_mode: paint_metadata.blend_mode,
|
||||
}
|
||||
}).collect();
|
||||
let mut render_commands = vec![RenderCommand::UploadTextureMetadata(texture_metadata)];
|
||||
}).collect()
|
||||
}
|
||||
|
||||
// Allocate textures.
|
||||
let mut texture_page_descriptors = vec![];
|
||||
for page_index in 0..self.allocator.page_count() {
|
||||
let page_id = TexturePageId(page_index);
|
||||
let page_size = self.allocator.page_size(page_id);
|
||||
fn allocate_textures(&self,
|
||||
render_commands: &mut Vec<RenderCommand>,
|
||||
texture_manager: &mut PaintTextureManager) {
|
||||
for page_id in texture_manager.allocator.page_ids() {
|
||||
let page_size = texture_manager.allocator.page_size(page_id);
|
||||
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 });
|
||||
self.allocator.mark_page_as_allocated(page_id);
|
||||
}
|
||||
}
|
||||
texture_manager.allocator.mark_all_pages_as_allocated();
|
||||
}
|
||||
|
||||
// Gather up render target metadata.
|
||||
let render_target_metadata: Vec<_> = self.render_targets.iter().map(|render_target_data| {
|
||||
render_target_data.metadata
|
||||
}).collect();
|
||||
|
||||
// Create render commands.
|
||||
fn create_render_commands(&self,
|
||||
render_commands: &mut Vec<RenderCommand>,
|
||||
render_target_metadata: &[RenderTargetMetadata],
|
||||
gradient_tile_builder: GradientTileBuilder,
|
||||
image_texel_info: Vec<ImageTexelInfo>) {
|
||||
for (index, metadata) in render_target_metadata.iter().enumerate() {
|
||||
let id = RenderTargetId { scene: self.scene_id.0, render_target: index as u32 };
|
||||
render_commands.push(RenderCommand::DeclareRenderTarget {
|
||||
|
@ -591,15 +677,36 @@ impl Palette {
|
|||
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 {
|
||||
render_commands.push(RenderCommand::UploadTexelData {
|
||||
texels: image_texel_info.texels,
|
||||
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 {
|
||||
|
@ -612,7 +719,7 @@ impl Palette {
|
|||
scene: palette.scene_id.0,
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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) render_target_mapping: HashMap<RenderTargetId, RenderTargetId>,
|
||||
pub(crate) paint_mapping: HashMap<PaintId, PaintId>,
|
||||
|
@ -702,15 +818,20 @@ impl GradientTileBuilder {
|
|||
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 {
|
||||
if self.tiles.is_empty() ||
|
||||
self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH {
|
||||
let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32);
|
||||
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 {
|
||||
texels: vec![ColorU::black(); area],
|
||||
page: allocator.allocate(size, AllocationMode::OwnPage).page,
|
||||
page: page_location.page,
|
||||
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 {
|
||||
location: TextureLocation,
|
||||
texels: Arc<Vec<ColorU>>,
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::gpu::renderer::Renderer;
|
|||
use crate::gpu_data::RenderCommand;
|
||||
use crate::options::{BuildOptions, PreparedBuildOptions};
|
||||
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::fill::FillRule;
|
||||
use pathfinder_content::outline::Outline;
|
||||
|
@ -178,8 +178,11 @@ impl Scene {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo {
|
||||
self.palette.build_paint_info(render_transform)
|
||||
pub(crate) fn build_paint_info(&mut self,
|
||||
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
|
||||
|
@ -382,6 +385,7 @@ pub struct SceneSink<'a> {
|
|||
pub(crate) listener: RenderCommandListener<'a>,
|
||||
pub(crate) renderer_level: RendererLevel,
|
||||
pub(crate) last_scene: Option<LastSceneInfo>,
|
||||
pub(crate) paint_texture_manager: PaintTextureManager,
|
||||
}
|
||||
|
||||
pub(crate) struct LastSceneInfo {
|
||||
|
@ -423,7 +427,12 @@ impl<'a> SceneSink<'a> {
|
|||
#[inline]
|
||||
pub fn new(listener: RenderCommandListener<'a>, renderer_level: RendererLevel)
|
||||
-> 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