iced_wgpu/image/
raster.rs

1use crate::core::Size;
2use crate::core::image;
3use crate::graphics;
4use crate::image::atlas::{self, Atlas};
5
6use rustc_hash::{FxHashMap, FxHashSet};
7use std::sync::{Arc, Weak};
8
9pub type Image = graphics::image::Buffer;
10
11/// Entry in cache corresponding to an image handle
12#[derive(Debug)]
13pub enum Memory {
14    /// Image data on host
15    Host(Image),
16    /// Storage entry
17    Device {
18        entry: atlas::Entry,
19        bind_group: Option<Arc<wgpu::BindGroup>>,
20        allocation: Option<Weak<image::Memory>>,
21    },
22    Error(image::Error),
23}
24
25impl Memory {
26    pub fn load(handle: &image::Handle) -> Self {
27        match graphics::image::load(handle) {
28            Ok(image) => Self::Host(image),
29            Err(error) => Self::Error(error),
30        }
31    }
32
33    pub fn dimensions(&self) -> Size<u32> {
34        match self {
35            Memory::Host(image) => {
36                let (width, height) = image.dimensions();
37
38                Size::new(width, height)
39            }
40            Memory::Device { entry, .. } => entry.size(),
41            Memory::Error(_) => Size::new(1, 1),
42        }
43    }
44
45    pub fn host(&self) -> Option<Image> {
46        match self {
47            Memory::Host(image) => Some(image.clone()),
48            Memory::Device { .. } | Memory::Error(_) => None,
49        }
50    }
51}
52
53#[derive(Debug, Default)]
54pub struct Cache {
55    map: FxHashMap<image::Id, Memory>,
56    hits: FxHashSet<image::Id>,
57    should_trim: bool,
58}
59
60impl Cache {
61    pub fn get_mut(&mut self, handle: &image::Handle) -> Option<&mut Memory> {
62        let _ = self.hits.insert(handle.id());
63
64        self.map.get_mut(&handle.id())
65    }
66
67    pub fn insert(&mut self, handle: &image::Handle, memory: Memory) {
68        let _ = self.map.insert(handle.id(), memory);
69        let _ = self.hits.insert(handle.id());
70
71        self.should_trim = true;
72    }
73
74    pub fn contains(&self, handle: &image::Handle) -> bool {
75        self.map.contains_key(&handle.id())
76    }
77
78    pub fn trim(&mut self, atlas: &mut Atlas, on_drop: impl Fn(Arc<wgpu::BindGroup>)) {
79        // Only trim if new entries have landed in the `Cache`
80        if !self.should_trim {
81            return;
82        }
83
84        let hits = &self.hits;
85
86        self.map.retain(|id, memory| {
87            // Retain active allocations
88            if let Memory::Device { allocation, .. } = memory
89                && allocation
90                    .as_ref()
91                    .is_some_and(|allocation| allocation.strong_count() > 0)
92            {
93                return true;
94            }
95
96            let retain = hits.contains(id);
97
98            if !retain {
99                log::debug!("Dropping image allocation: {id:?}");
100
101                if let Memory::Device {
102                    entry, bind_group, ..
103                } = memory
104                {
105                    if let Some(bind_group) = bind_group.take() {
106                        on_drop(bind_group);
107                    } else {
108                        atlas.remove(entry);
109                    }
110                }
111            }
112
113            retain
114        });
115
116        self.hits.clear();
117        self.should_trim = false;
118    }
119}