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}