From 493c36ac712ef04523065b94988a88cc4db16b1a Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Wed, 24 Apr 2024 21:29:30 +0200
Subject: Make image `Cache` eviction strategy less aggressive in `iced_wgpu`

Instead of trimming unconditionally at the end of
a frame, we now trim the cache only when there is a
cache miss.

This way, images that are not visible but still a
part of the layout will stay cached. Eviction will
only happen when the images are not a part of the
UI for two consectuive frames.
---
 wgpu/src/image/atlas.rs           | 15 +++++++++++----
 wgpu/src/image/atlas/allocator.rs |  4 ++++
 wgpu/src/image/atlas/layer.rs     |  8 ++++++++
 wgpu/src/image/mod.rs             |  2 +-
 wgpu/src/image/raster.rs          |  9 +++++++++
 wgpu/src/image/vector.rs          |  8 ++++++++
 6 files changed, 41 insertions(+), 5 deletions(-)

(limited to 'wgpu/src/image')

diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs
index ea36e06d..ae43c3b4 100644
--- a/wgpu/src/image/atlas.rs
+++ b/wgpu/src/image/atlas.rs
@@ -94,7 +94,7 @@ impl Atlas {
             entry
         };
 
-        log::info!("Allocated atlas entry: {entry:?}");
+        log::debug!("Allocated atlas entry: {entry:?}");
 
         // It is a webgpu requirement that:
         //   BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0
@@ -147,13 +147,20 @@ impl Atlas {
             }
         }
 
-        log::info!("Current atlas: {self:?}");
+        if log::log_enabled!(log::Level::Debug) {
+            log::debug!(
+                "Atlas layers: {} (busy: {}, allocations: {})",
+                self.layer_count(),
+                self.layers.iter().filter(|layer| !layer.is_empty()).count(),
+                self.layers.iter().map(Layer::allocations).sum::<usize>(),
+            );
+        }
 
         Some(entry)
     }
 
     pub fn remove(&mut self, entry: &Entry) {
-        log::info!("Removing atlas entry: {entry:?}");
+        log::debug!("Removing atlas entry: {entry:?}");
 
         match entry {
             Entry::Contiguous(allocation) => {
@@ -266,7 +273,7 @@ impl Atlas {
     }
 
     fn deallocate(&mut self, allocation: &Allocation) {
-        log::info!("Deallocating atlas: {allocation:?}");
+        log::debug!("Deallocating atlas: {allocation:?}");
 
         match allocation {
             Allocation::Full { layer } => {
diff --git a/wgpu/src/image/atlas/allocator.rs b/wgpu/src/image/atlas/allocator.rs
index 204a5c26..a51ac1f5 100644
--- a/wgpu/src/image/atlas/allocator.rs
+++ b/wgpu/src/image/atlas/allocator.rs
@@ -33,6 +33,10 @@ impl Allocator {
     pub fn is_empty(&self) -> bool {
         self.allocations == 0
     }
+
+    pub fn allocations(&self) -> usize {
+        self.allocations
+    }
 }
 
 pub struct Region {
diff --git a/wgpu/src/image/atlas/layer.rs b/wgpu/src/image/atlas/layer.rs
index cf089601..fd6788d9 100644
--- a/wgpu/src/image/atlas/layer.rs
+++ b/wgpu/src/image/atlas/layer.rs
@@ -11,4 +11,12 @@ impl Layer {
     pub fn is_empty(&self) -> bool {
         matches!(self, Layer::Empty)
     }
+
+    pub fn allocations(&self) -> usize {
+        match self {
+            Layer::Empty => 0,
+            Layer::Busy(allocator) => allocator.allocations(),
+            Layer::Full => 1,
+        }
+    }
 }
diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs
index 86731cbf..8b831a3c 100644
--- a/wgpu/src/image/mod.rs
+++ b/wgpu/src/image/mod.rs
@@ -277,7 +277,7 @@ impl Pipeline {
         let texture_version = cache.layer_count();
 
         if self.texture_version != texture_version {
-            log::info!("Atlas has grown. Recreating bind group...");
+            log::debug!("Atlas has grown. Recreating bind group...");
 
             self.texture =
                 cache.create_bind_group(device, &self.texture_layout);
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 441b294f..7a837f28 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -40,6 +40,7 @@ impl Memory {
 pub struct Cache {
     map: FxHashMap<u64, Memory>,
     hits: FxHashSet<u64>,
+    should_trim: bool,
 }
 
 impl Cache {
@@ -55,6 +56,8 @@ impl Cache {
             Err(_) => Memory::Invalid,
         };
 
+        self.should_trim = true;
+
         self.insert(handle, memory);
         self.get(handle).unwrap()
     }
@@ -86,6 +89,11 @@ impl Cache {
 
     /// Trim cache misses from cache
     pub fn trim(&mut self, atlas: &mut Atlas) {
+        // Only trim if new entries have landed in the `Cache`
+        if !self.should_trim {
+            return;
+        }
+
         let hits = &self.hits;
 
         self.map.retain(|k, memory| {
@@ -101,6 +109,7 @@ impl Cache {
         });
 
         self.hits.clear();
+        self.should_trim = false;
     }
 
     fn get(&mut self, handle: &image::Handle) -> Option<&mut Memory> {
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index d681b2e6..c6d829af 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -37,6 +37,7 @@ pub struct Cache {
     rasterized: FxHashMap<(u64, u32, u32, ColorFilter), atlas::Entry>,
     svg_hits: FxHashSet<u64>,
     rasterized_hits: FxHashSet<(u64, u32, u32, ColorFilter)>,
+    should_trim: bool,
 }
 
 type ColorFilter = Option<[u8; 4]>;
@@ -76,6 +77,8 @@ impl Cache {
             }
         }
 
+        self.should_trim = true;
+
         let _ = self.svgs.insert(handle.id(), svg);
         self.svgs.get(&handle.id()).unwrap()
     }
@@ -176,6 +179,10 @@ impl Cache {
 
     /// Load svg and upload raster data
     pub fn trim(&mut self, atlas: &mut Atlas) {
+        if !self.should_trim {
+            return;
+        }
+
         let svg_hits = &self.svg_hits;
         let rasterized_hits = &self.rasterized_hits;
 
@@ -191,6 +198,7 @@ impl Cache {
         });
         self.svg_hits.clear();
         self.rasterized_hits.clear();
+        self.should_trim = false;
     }
 }
 
-- 
cgit