Skip to main content

dear_imgui_wgpu/texture/
cleanup.rs

1use super::result::mark_texture_destroyed;
2use super::*;
3
4impl WgpuTextureManager {
5    /// Destroy a texture by ID
6    pub fn destroy_texture_by_id(&mut self, id: TextureId) {
7        self.remove_texture(id);
8    }
9
10    /// Destroy a texture
11    pub fn destroy_texture(&mut self, texture_id: TextureId) {
12        self.remove_texture(texture_id);
13        // WGPU textures are automatically cleaned up when dropped
14    }
15
16    /// Handle texture updates from Dear ImGui draw data
17    ///
18    /// This iterates `DrawData::textures_mut()` and applies create/update/destroy requests.
19    /// For `WantCreate`, we create the GPU texture, then write the generated id back into
20    /// the `ImTextureData` via `set_tex_id()` and mark status `OK` (matching C++ backend).
21    /// For `WantUpdates`, if a valid id is not yet assigned (first use), we create now and
22    /// assign the id; otherwise we update in place. When textures are recreated or destroyed,
23    /// the corresponding cached bind groups in `RenderResources` are invalidated so that
24    /// subsequent draws will see the updated views.
25    pub fn handle_texture_updates(
26        &mut self,
27        draw_data: &mut dear_imgui_rs::render::DrawData,
28        device: &Device,
29        queue: &Queue,
30        render_resources: &mut RenderResources,
31    ) {
32        let mut textures = draw_data.textures_mut();
33        while let Some(mut texture_data) = textures.next() {
34            let status = texture_data.status();
35            let current_tex_id = texture_data.tex_id();
36
37            match status {
38                TextureStatus::WantCreate => {
39                    // Create and upload new texture to graphics system
40                    // Following the official imgui_impl_wgpu.cpp implementation
41
42                    // If ImGui already had a TexID associated, drop any stale bind group
43                    // so that a new one is created the first time we render with it.
44                    if !current_tex_id.is_null() {
45                        render_resources.remove_image_bind_group(current_tex_id);
46                    }
47
48                    match self.create_texture_from_data(device, queue, &*texture_data) {
49                        Ok(wgpu_texture_id) => {
50                            // CRITICAL: Set the texture ID back to Dear ImGui
51                            // In the C++ implementation, they use the TextureView pointer as ImTextureID.
52                            // In Rust, we can't get the raw pointer, so we use our internal texture ID.
53                            // This works because our renderer will map the texture ID to the WGPU texture.
54                            texture_data.set_tex_id(wgpu_texture_id);
55
56                            // Mark texture as ready
57                            texture_data.set_status(TextureStatus::OK);
58                        }
59                        Err(e) => {
60                            println!(
61                                "Failed to create texture for ID: {:?}, error: {}",
62                                current_tex_id, e
63                            );
64                        }
65                    }
66                }
67                TextureStatus::WantUpdates => {
68                    let imgui_tex_id = texture_data.tex_id();
69                    let internal_id = imgui_tex_id;
70
71                    // If we don't have a valid texture id yet (first update) or the
72                    // id isn't registered, create it now and write back the TexID,
73                    // so this frame (or the next one) can bind the correct texture.
74                    if internal_id.is_null() || !self.contains_texture(internal_id) {
75                        match self.create_texture_from_data(device, queue, &*texture_data) {
76                            Ok(new_id) => {
77                                texture_data.set_tex_id(new_id);
78                                texture_data.set_status(TextureStatus::OK);
79                            }
80                            Err(_e) => {
81                                // Leave it destroyed to avoid retry storm; user can request create again
82                                texture_data.set_status(TextureStatus::Destroyed);
83                            }
84                        }
85                    } else {
86                        // We are about to update/recreate an existing texture. Invalidate
87                        // any cached bind group so it will be rebuilt with the new view.
88                        render_resources.remove_image_bind_group(internal_id);
89
90                        // Try in-place sub-rect updates first
91                        if self
92                            .apply_subrect_updates(queue, &*texture_data, internal_id)
93                            .unwrap_or(false)
94                        {
95                            texture_data.set_status(TextureStatus::OK);
96                        } else if self
97                            .update_texture_from_data_with_id(
98                                device,
99                                queue,
100                                &*texture_data,
101                                internal_id,
102                            )
103                            .is_err()
104                        {
105                            // If update fails, keep the existing GPU texture and mark OK to avoid a retry storm.
106                            // We cannot clear TexID here because draw commands in this frame may still reference it.
107                            texture_data.set_status(TextureStatus::OK);
108                        } else {
109                            texture_data.set_status(TextureStatus::OK);
110                        }
111                    }
112                }
113                TextureStatus::WantDestroy => {
114                    // Only destroy when unused frames > 0 (align with official backend behavior)
115                    let mut can_destroy = true;
116                    unsafe {
117                        let raw = texture_data.as_raw();
118                        if !raw.is_null() {
119                            // If field not present in bindings on some versions, default true
120                            #[allow(unused_unsafe)]
121                            {
122                                // Access UnusedFrames if available
123                                // SAFETY: reading a plain field from raw C struct
124                                can_destroy = (*raw).UnusedFrames > 0;
125                            }
126                        }
127                    }
128                    if can_destroy {
129                        let imgui_tex_id = texture_data.tex_id();
130                        let internal_id = imgui_tex_id;
131                        // Remove from texture cache and any associated bind groups
132                        self.remove_texture(internal_id);
133                        self.clear_custom_sampler_for_texture(internal_id);
134                        render_resources.remove_image_bind_group(internal_id);
135                        mark_texture_destroyed(&mut texture_data);
136                    }
137                }
138                TextureStatus::OK | TextureStatus::Destroyed => {
139                    // No action needed
140                }
141            }
142        }
143    }
144}