Skip to content

Commit 83c725e

Browse files
authored
Auto merge of #394 - pcwalton:image-caching, r=pcwalton
Cache images from frame to frame. Images are cached for one frame; if they are not used the next frame, they're freed.
2 parents f62f853 + bb89f52 commit 83c725e

File tree

6 files changed

+361
-135
lines changed

6 files changed

+361
-135
lines changed

content/src/pattern.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ pub struct Image {
5353
is_opaque: bool,
5454
}
5555

56+
/// Unique identifier for an image.
57+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
58+
pub struct ImageHash(pub u64);
59+
5660
bitflags! {
5761
pub struct PatternFlags: u8 {
5862
const REPEAT_X = 0x01;
@@ -185,6 +189,13 @@ impl Image {
185189
pub fn is_opaque(&self) -> bool {
186190
self.is_opaque
187191
}
192+
193+
#[inline]
194+
pub fn get_hash(&self) -> ImageHash {
195+
let mut hasher = DefaultHasher::new();
196+
self.hash(&mut hasher);
197+
ImageHash(hasher.finish())
198+
}
188199
}
189200

190201
impl PatternSource {

examples/canvas_nanovg/src/main.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,6 +1541,9 @@ fn main() {
15411541
let mut cpu_graph = PerfGraph::new(GraphStyle::MS, "CPU Time");
15421542
let mut gpu_graph = PerfGraph::new(GraphStyle::MS, "GPU Time");
15431543

1544+
// Initialize scene proxy.
1545+
let mut scene = SceneProxy::new(renderer.mode().level, RayonExecutor);
1546+
15441547
// Enter the main loop.
15451548
let mut exit = false;
15461549
while !exit {
@@ -1569,9 +1572,7 @@ fn main() {
15691572

15701573
// Render the canvas to screen.
15711574
let canvas = context.into_canvas();
1572-
let mut scene = SceneProxy::from_scene(canvas.into_scene(),
1573-
renderer.mode().level,
1574-
RayonExecutor);
1575+
scene.replace_scene(canvas.into_scene());
15751576
scene.build_and_render(&mut renderer, BuildOptions::default());
15761577

15771578
// Present the rendered canvas via `surfman`.

renderer/src/allocator.rs

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const ATLAS_TEXTURE_LENGTH: u32 = 1024;
1818

1919
#[derive(Clone, Debug)]
2020
pub struct TextureAllocator {
21-
pages: Vec<TexturePage>,
21+
pages: Vec<Option<TexturePage>>,
2222
}
2323

2424
#[derive(Clone, Debug)]
@@ -71,40 +71,87 @@ impl TextureAllocator {
7171
}
7272

7373
// Try to add to each atlas.
74+
let mut first_free_page_index = self.pages.len();
7475
for (page_index, page) in self.pages.iter_mut().enumerate() {
75-
match page.allocator {
76-
TexturePageAllocator::Image { .. } => {}
77-
TexturePageAllocator::Atlas(ref mut allocator) => {
78-
if let Some(rect) = allocator.allocate(requested_size) {
79-
return TextureLocation { page: TexturePageId(page_index as u32), rect };
76+
match *page {
77+
Some(ref mut page) => {
78+
match page.allocator {
79+
TexturePageAllocator::Image { .. } => {}
80+
TexturePageAllocator::Atlas(ref mut allocator) => {
81+
if let Some(rect) = allocator.allocate(requested_size) {
82+
return TextureLocation {
83+
page: TexturePageId(page_index as u32),
84+
rect
85+
};
86+
}
87+
}
8088
}
8189
}
90+
None => first_free_page_index = first_free_page_index.min(page_index),
8291
}
8392
}
8493

8594
// Add a new atlas.
86-
let page = TexturePageId(self.pages.len() as u32);
95+
let page = self.get_first_free_page_id();
8796
let mut allocator = TextureAtlasAllocator::new();
8897
let rect = allocator.allocate(requested_size).expect("Allocation failed!");
89-
self.pages.push(TexturePage {
98+
while (page.0 as usize) >= self.pages.len() {
99+
self.pages.push(None);
100+
}
101+
self.pages[page.0 as usize] = Some(TexturePage {
90102
is_new: true,
91103
allocator: TexturePageAllocator::Atlas(allocator),
92104
});
93105
TextureLocation { page, rect }
94106
}
95107

96108
pub fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation {
97-
let page = TexturePageId(self.pages.len() as u32);
109+
let page = self.get_first_free_page_id();
98110
let rect = RectI::new(Vector2I::default(), requested_size);
99-
self.pages.push(TexturePage {
111+
while (page.0 as usize) >= self.pages.len() {
112+
self.pages.push(None);
113+
}
114+
self.pages[page.0 as usize] = Some(TexturePage {
100115
is_new: true,
101116
allocator: TexturePageAllocator::Image { size: rect.size() },
102117
});
103118
TextureLocation { page, rect }
104119
}
105120

121+
fn get_first_free_page_id(&self) -> TexturePageId {
122+
for (page_index, page) in self.pages.iter().enumerate() {
123+
if page.is_none() {
124+
return TexturePageId(page_index as u32);
125+
}
126+
}
127+
TexturePageId(self.pages.len() as u32)
128+
}
129+
130+
pub fn free(&mut self, location: TextureLocation) {
131+
//println!("free({:?})", location);
132+
match self.pages[location.page.0 as usize]
133+
.as_mut()
134+
.expect("Texture page is not allocated!")
135+
.allocator {
136+
TexturePageAllocator::Image { size } => {
137+
debug_assert_eq!(location.rect, RectI::new(Vector2I::default(), size));
138+
}
139+
TexturePageAllocator::Atlas(ref mut atlas_allocator) => {
140+
atlas_allocator.free(location.rect);
141+
if !atlas_allocator.is_empty() {
142+
// Keep the page around.
143+
return;
144+
}
145+
}
146+
}
147+
148+
// If we got here, free the page.
149+
// TODO(pcwalton): Actually tell the renderer to free this page!
150+
self.pages[location.page.0 as usize] = None;
151+
}
152+
106153
pub fn page_size(&self, page_id: TexturePageId) -> Vector2I {
107-
match self.pages[page_id.0 as usize].allocator {
154+
match self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").allocator {
108155
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
109156
TexturePageAllocator::Image { size, .. } => size,
110157
}
@@ -115,16 +162,23 @@ impl TextureAllocator {
115162
}
116163

117164
pub fn page_is_new(&self, page_id: TexturePageId) -> bool {
118-
self.pages[page_id.0 as usize].is_new
165+
self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").is_new
119166
}
120167

121-
pub fn mark_page_as_allocated(&mut self, page_id: TexturePageId) {
122-
self.pages[page_id.0 as usize].is_new = false;
168+
pub fn mark_all_pages_as_allocated(&mut self) {
169+
for page in &mut self.pages {
170+
if let Some(ref mut page) = *page {
171+
page.is_new = false;
172+
}
173+
}
123174
}
124175

125-
#[inline]
126-
pub fn page_count(&self) -> u32 {
127-
self.pages.len() as u32
176+
pub fn page_ids(&self) -> TexturePageIter {
177+
let mut first_index = 0;
178+
while first_index < self.pages.len() && self.pages[first_index].is_none() {
179+
first_index += 1;
180+
}
181+
TexturePageIter { allocator: self, next_index: first_index }
128182
}
129183
}
130184

@@ -147,7 +201,6 @@ impl TextureAtlasAllocator {
147201
}
148202

149203
#[inline]
150-
#[allow(dead_code)]
151204
fn free(&mut self, rect: RectI) {
152205
let requested_length = rect.width() as u32;
153206
self.root.free(Vector2I::default(), self.size, rect.origin(), requested_length)
@@ -285,6 +338,30 @@ impl TreeNode {
285338
}
286339
}
287340

341+
pub struct TexturePageIter<'a> {
342+
allocator: &'a TextureAllocator,
343+
next_index: usize,
344+
}
345+
346+
impl<'a> Iterator for TexturePageIter<'a> {
347+
type Item = TexturePageId;
348+
fn next(&mut self) -> Option<TexturePageId> {
349+
let next_id = if self.next_index >= self.allocator.pages.len() {
350+
None
351+
} else {
352+
Some(TexturePageId(self.next_index as u32))
353+
};
354+
loop {
355+
self.next_index += 1;
356+
if self.next_index >= self.allocator.pages.len() ||
357+
self.allocator.pages[self.next_index as usize].is_some() {
358+
break;
359+
}
360+
}
361+
next_id
362+
}
363+
}
364+
288365
#[cfg(test)]
289366
mod test {
290367
use pathfinder_geometry::vector::vec2i;

renderer/src/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl<'a, 'b, 'c, 'd> SceneBuilder<'a, 'b, 'c, 'd> {
175175
render_commands,
176176
paint_metadata,
177177
render_target_metadata: _,
178-
} = self.scene.build_paint_info(render_transform);
178+
} = self.scene.build_paint_info(&mut self.sink.paint_texture_manager, render_transform);
179179
for render_command in render_commands {
180180
self.sink.listener.send(render_command);
181181
}

0 commit comments

Comments
 (0)