egui/load/
texture_loader.rs1use emath::Vec2;
2
3use super::{
4 BytesLoader as _, Context, HashMap, ImagePoll, Mutex, SizeHint, SizedTexture, TextureHandle,
5 TextureLoadResult, TextureLoader, TextureOptions, TexturePoll,
6};
7
8#[derive(Clone, Debug, PartialEq, Eq, Hash)]
9struct PrimaryKey {
10 uri: String,
11 texture_options: TextureOptions,
12}
13
14type Bucket = HashMap<Option<SizeHint>, Entry>;
16
17struct Entry {
18 last_used: u64,
19
20 source_size: Vec2,
22
23 handle: TextureHandle,
24}
25
26#[derive(Default)]
27struct State {
28 pass_index: u64,
29 cache: HashMap<PrimaryKey, Bucket>,
30}
31
32#[derive(Default)]
33pub struct DefaultTextureLoader {
34 state: Mutex<State>,
35}
36
37impl TextureLoader for DefaultTextureLoader {
38 fn id(&self) -> &'static str {
39 crate::generate_loader_id!(DefaultTextureLoader)
40 }
41
42 fn load(
43 &self,
44 ctx: &Context,
45 uri: &str,
46 texture_options: TextureOptions,
47 size_hint: SizeHint,
48 ) -> TextureLoadResult {
49 let svg_size_hint = if is_svg(uri) {
50 Some(size_hint)
56 } else {
57 None
59 };
60
61 let mut state = self.state.lock();
62 let State { pass_index, cache } = &mut *state;
63
64 let bucket = cache
65 .entry(PrimaryKey {
66 uri: uri.to_owned(),
67 texture_options,
68 })
69 .or_default();
70
71 if let Some(texture) = bucket.get_mut(&svg_size_hint) {
72 texture.last_used = *pass_index;
73 let texture = SizedTexture::new(texture.handle.id(), texture.source_size);
74 Ok(TexturePoll::Ready { texture })
75 } else {
76 match ctx.try_load_image(uri, size_hint)? {
77 ImagePoll::Pending { size } => Ok(TexturePoll::Pending { size }),
78 ImagePoll::Ready { image } => {
79 let source_size = image.source_size;
80 let handle = ctx.load_texture(uri, image, texture_options);
81 let texture = SizedTexture::new(handle.id(), source_size);
82 bucket.insert(
83 svg_size_hint,
84 Entry {
85 last_used: *pass_index,
86 source_size,
87 handle,
88 },
89 );
90 let reduce_texture_memory = ctx.options(|o| o.reduce_texture_memory);
91 if reduce_texture_memory {
92 let loaders = ctx.loaders();
93 loaders.include.forget(uri);
94 for loader in loaders.bytes.lock().iter().rev() {
95 loader.forget(uri);
96 }
97 for loader in loaders.image.lock().iter().rev() {
98 loader.forget(uri);
99 }
100 }
101 Ok(TexturePoll::Ready { texture })
102 }
103 }
104 }
105 }
106
107 fn forget(&self, uri: &str) {
108 log::trace!("forget {uri:?}");
109
110 self.state.lock().cache.retain(|key, _value| key.uri != uri);
111 }
112
113 fn forget_all(&self) {
114 log::trace!("forget all");
115
116 self.state.lock().cache.clear();
117 }
118
119 fn end_pass(&self, pass_index: u64) {
120 let mut state = self.state.lock();
121 state.pass_index = pass_index;
122
123 let State { pass_index, cache } = &mut *state;
124
125 cache.retain(|_key, bucket| {
126 if 2 <= bucket.len() {
127 bucket.retain(|_, texture| *pass_index <= texture.last_used + 1);
132 }
133 !bucket.is_empty()
134 });
135 }
136
137 fn byte_size(&self) -> usize {
138 self.state
139 .lock()
140 .cache
141 .values()
142 .map(|bucket| {
143 bucket
144 .values()
145 .map(|texture| texture.handle.byte_size())
146 .sum::<usize>()
147 })
148 .sum()
149 }
150}
151
152fn is_svg(uri: &str) -> bool {
153 super::has_extension(uri, "svg")
154}