Skip to main content

dear_imgui_wgpu/texture/
cache.rs

1use super::*;
2
3impl WgpuTextureManager {
4    /// Create a new texture manager
5    pub fn new() -> Self {
6        Self {
7            textures: HashMap::new(),
8            next_id: 1, // Start from 1, 0 is reserved for null texture
9            custom_samplers: HashMap::new(),
10            custom_sampler_by_texture: HashMap::new(),
11            common_bind_groups: HashMap::new(),
12            next_sampler_id: 1, // Start from 1, 0 means "default sampler"
13        }
14    }
15
16    /// Register a new texture and return its ID
17    pub fn register_texture(&mut self, texture: WgpuTexture) -> TextureId {
18        let id = TextureId::new(self.next_id);
19        self.next_id += 1;
20        self.textures.insert(id, texture);
21        id
22    }
23
24    /// Get a texture by ID
25    pub fn get_texture(&self, id: TextureId) -> Option<&WgpuTexture> {
26        self.textures.get(&id)
27    }
28
29    /// Remove a texture by ID
30    pub fn remove_texture(&mut self, id: TextureId) -> Option<WgpuTexture> {
31        self.textures.remove(&id)
32    }
33
34    /// Check if a texture exists
35    pub fn contains_texture(&self, id: TextureId) -> bool {
36        self.textures.contains_key(&id)
37    }
38
39    /// Insert a texture with a specific ID
40    pub fn insert_texture_with_id(&mut self, id: TextureId, texture: WgpuTexture) {
41        self.textures.insert(id, texture);
42        // Update next_id if necessary
43        if id.id() >= self.next_id {
44            self.next_id = id.id().saturating_add(1);
45        }
46    }
47
48    /// Associate a custom sampler with a texture id (used by external textures).
49    ///
50    /// Returns the internal sampler_id assigned to this sampler.
51    pub(crate) fn set_custom_sampler_for_texture(
52        &mut self,
53        texture_id: TextureId,
54        sampler: Sampler,
55    ) -> u64 {
56        let sampler_id = self.next_sampler_id;
57        self.next_sampler_id += 1;
58        self.custom_samplers.insert(sampler_id, sampler);
59        self.custom_sampler_by_texture
60            .insert(texture_id, sampler_id);
61        // Invalidate any cached common bind group for this sampler id (defensive).
62        self.common_bind_groups.remove(&sampler_id);
63        sampler_id
64    }
65
66    /// Update or set a custom sampler for an existing texture.
67    ///
68    /// If the texture already has a custom sampler association, we replace the sampler
69    /// in place (keeping the sampler_id stable) and invalidate the cached common bind group.
70    /// If there is no association yet, we create one.
71    ///
72    /// Returns false if the texture_id is not registered.
73    pub(crate) fn update_custom_sampler_for_texture(
74        &mut self,
75        texture_id: TextureId,
76        sampler: Sampler,
77    ) -> bool {
78        if !self.textures.contains_key(&texture_id) {
79            return false;
80        }
81        if let Some(sampler_id) = self.custom_sampler_by_texture.get(&texture_id).copied() {
82            self.custom_samplers.insert(sampler_id, sampler);
83            self.common_bind_groups.remove(&sampler_id);
84        } else {
85            self.set_custom_sampler_for_texture(texture_id, sampler);
86        }
87        true
88    }
89
90    /// Get the custom sampler id for a texture (if any).
91    pub(crate) fn custom_sampler_id_for_texture(&self, texture_id: TextureId) -> Option<u64> {
92        self.custom_sampler_by_texture.get(&texture_id).copied()
93    }
94
95    /// Remove any custom sampler association for a texture.
96    pub(crate) fn clear_custom_sampler_for_texture(&mut self, texture_id: TextureId) {
97        if let Some(sampler_id) = self.custom_sampler_by_texture.remove(&texture_id) {
98            // Drop cached bind group so next use rebuilds it.
99            self.common_bind_groups.remove(&sampler_id);
100        }
101    }
102
103    /// Get or create a common bind group (uniform buffer + sampler) for the given sampler id.
104    ///
105    /// The bind group uses the same uniform buffer but swaps the sampler, allowing
106    /// per-texture sampling without changing the pipeline layout.
107    pub(crate) fn get_or_create_common_bind_group_for_sampler(
108        &mut self,
109        device: &Device,
110        common_layout: &BindGroupLayout,
111        uniform_buffer: &Buffer,
112        sampler_id: u64,
113    ) -> Option<BindGroup> {
114        if let Some(bg) = self.common_bind_groups.get(&sampler_id) {
115            return Some(bg.clone());
116        }
117        let sampler = self.custom_samplers.get(&sampler_id)?;
118        let bg = device.create_bind_group(&BindGroupDescriptor {
119            label: Some("Dear ImGui Common Bind Group (custom sampler)"),
120            layout: common_layout,
121            entries: &[
122                BindGroupEntry {
123                    binding: 0,
124                    resource: uniform_buffer.as_entire_binding(),
125                },
126                BindGroupEntry {
127                    binding: 1,
128                    resource: BindingResource::Sampler(sampler),
129                },
130            ],
131        });
132        self.common_bind_groups.insert(sampler_id, bg.clone());
133        Some(bg)
134    }
135
136    /// Get the number of registered textures
137    pub fn texture_count(&self) -> usize {
138        self.textures.len()
139    }
140
141    /// Clear all textures
142    pub fn clear(&mut self) {
143        self.textures.clear();
144        self.next_id = 1;
145        self.custom_sampler_by_texture.clear();
146        self.common_bind_groups.clear();
147        // Keep samplers around? Clear to avoid holding stale handles after device loss.
148        self.custom_samplers.clear();
149        self.next_sampler_id = 1;
150    }
151}