Skip to main content

dear_imgui_rs/context/
texture_registry.rs

1use std::cell::RefCell;
2use std::rc::{Rc, Weak};
3
4use crate::sys;
5
6use super::Context;
7use super::binding::{CTX_MUTEX, with_bound_context};
8
9#[derive(Clone)]
10struct UserTextureRegistration {
11    ctx: *mut sys::ImGuiContext,
12    tex: *mut sys::ImTextureData,
13    alive: Weak<()>,
14}
15
16thread_local! {
17    static USER_TEXTURE_REGISTRATIONS: RefCell<Vec<UserTextureRegistration>> = RefCell::new(Vec::new());
18}
19
20fn prune_dead_user_texture_registrations(registrations: &mut Vec<UserTextureRegistration>) {
21    registrations.retain(|registration| registration.alive.upgrade().is_some());
22}
23
24fn is_user_texture_registered(ctx: *mut sys::ImGuiContext, tex: *mut sys::ImTextureData) -> bool {
25    USER_TEXTURE_REGISTRATIONS.with(|registrations| {
26        let mut registrations = registrations.borrow_mut();
27        prune_dead_user_texture_registrations(&mut registrations);
28        registrations
29            .iter()
30            .any(|registration| registration.ctx == ctx && registration.tex == tex)
31    })
32}
33
34fn track_user_texture_registration(
35    ctx: *mut sys::ImGuiContext,
36    tex: *mut sys::ImTextureData,
37    alive: Weak<()>,
38) {
39    USER_TEXTURE_REGISTRATIONS.with(|registrations| {
40        let mut registrations = registrations.borrow_mut();
41        prune_dead_user_texture_registrations(&mut registrations);
42        registrations.push(UserTextureRegistration { ctx, tex, alive });
43    });
44}
45
46fn take_user_texture_registration(
47    ctx: *mut sys::ImGuiContext,
48    tex: *mut sys::ImTextureData,
49) -> Option<UserTextureRegistration> {
50    USER_TEXTURE_REGISTRATIONS.with(|registrations| {
51        let mut registrations = registrations.borrow_mut();
52        prune_dead_user_texture_registrations(&mut registrations);
53        registrations
54            .iter()
55            .position(|registration| registration.ctx == ctx && registration.tex == tex)
56            .map(|index| registrations.remove(index))
57    })
58}
59
60fn unregister_user_texture_registration(registration: UserTextureRegistration) {
61    if registration.ctx.is_null()
62        || registration.tex.is_null()
63        || registration.alive.upgrade().is_none()
64    {
65        return;
66    }
67
68    unsafe {
69        with_bound_context(registration.ctx, || {
70            sys::igUnregisterUserTexture(registration.tex);
71        });
72    }
73}
74
75pub(crate) fn unregister_user_texture_from_all_contexts(tex: *mut sys::ImTextureData) {
76    if tex.is_null() {
77        return;
78    }
79
80    let registrations = USER_TEXTURE_REGISTRATIONS.with(|registrations| {
81        let mut registrations = registrations.borrow_mut();
82        let mut taken = Vec::new();
83        let mut index = 0;
84        while index < registrations.len() {
85            if registrations[index].alive.upgrade().is_none() {
86                registrations.remove(index);
87            } else if registrations[index].tex == tex {
88                taken.push(registrations.remove(index));
89            } else {
90                index += 1;
91            }
92        }
93        taken
94    });
95
96    let _guard = CTX_MUTEX.lock();
97    for registration in registrations {
98        unregister_user_texture_registration(registration);
99    }
100}
101
102pub(super) fn unregister_user_textures_for_context(ctx: *mut sys::ImGuiContext) {
103    if ctx.is_null() {
104        return;
105    }
106
107    let registrations = USER_TEXTURE_REGISTRATIONS.with(|registrations| {
108        let mut registrations = registrations.borrow_mut();
109        let mut taken = Vec::new();
110        let mut index = 0;
111        while index < registrations.len() {
112            if registrations[index].alive.upgrade().is_none() || registrations[index].ctx == ctx {
113                let registration = registrations.remove(index);
114                if registration.ctx == ctx {
115                    taken.push(registration);
116                }
117            } else {
118                index += 1;
119            }
120        }
121        taken
122    });
123
124    for registration in registrations {
125        unregister_user_texture_registration(registration);
126    }
127}
128
129impl Context {
130    /// Register a user-created texture in ImGui's global texture list (ImGui 1.92+).
131    ///
132    /// Dear ImGui builds `DrawData::textures()` from its internal `PlatformIO.Textures[]` list.
133    /// If you create an `OwnedTextureData` yourself, you must register
134    /// it for renderer backends (with `BackendFlags::RENDERER_HAS_TEXTURES`) to receive
135    /// Create/Update/Destroy requests automatically.
136    ///
137    /// Note: `RegisterUserTexture()` is currently an experimental ImGui API.
138    ///
139    /// The registration is tracked by this crate and will be removed automatically when the
140    /// `Context` or the `OwnedTextureData` is dropped.
141    pub fn register_user_texture(&mut self, texture: &mut crate::texture::OwnedTextureData) {
142        self.register_user_texture_ptr(texture.as_mut().as_raw_mut());
143    }
144
145    /// Register a borrowed/raw texture data pointer in ImGui's global texture list.
146    ///
147    /// Prefer [`Context::register_user_texture`] for `OwnedTextureData`.
148    ///
149    /// # Safety
150    /// The caller must guarantee that `texture` remains alive until it is unregistered, the
151    /// owning `Context` is dropped, or the texture owner unregisters it from all contexts before
152    /// destruction.
153    pub unsafe fn register_user_texture_raw(&mut self, texture: &mut crate::texture::TextureData) {
154        self.register_user_texture_ptr(texture.as_raw_mut());
155    }
156
157    fn register_user_texture_ptr(&mut self, texture: *mut sys::ImTextureData) {
158        let _guard = CTX_MUTEX.lock();
159        self.assert_current_context("Context::register_user_texture()");
160        assert!(
161            !texture.is_null(),
162            "Context::register_user_texture() received a null texture"
163        );
164        if is_user_texture_registered(self.raw, texture) {
165            return;
166        }
167        unsafe {
168            sys::igRegisterUserTexture(texture);
169        }
170        track_user_texture_registration(self.raw, texture, Rc::downgrade(&self.alive));
171    }
172
173    /// Register a user-created texture and return an RAII token which unregisters on drop.
174    ///
175    /// This is a convenience wrapper around `register_user_texture()`.
176    pub fn register_user_texture_token(
177        &mut self,
178        texture: &mut crate::texture::OwnedTextureData,
179    ) -> RegisteredUserTexture {
180        self.register_user_texture(texture);
181        RegisteredUserTexture {
182            ctx: self.raw,
183            tex: texture.as_mut().as_raw_mut(),
184            alive: Rc::downgrade(&self.alive),
185        }
186    }
187
188    /// Unregister a user texture previously registered with `register_user_texture()`.
189    ///
190    /// This removes the `ImTextureData*` from ImGui's internal texture list.
191    pub fn unregister_user_texture(&mut self, texture: &mut crate::texture::OwnedTextureData) {
192        self.unregister_user_texture_ptr(texture.as_mut().as_raw_mut());
193    }
194
195    /// Unregister a borrowed/raw user texture previously registered with
196    /// [`Context::register_user_texture_raw`].
197    ///
198    /// # Safety
199    /// The pointer must refer to the same live `TextureData` object that was previously
200    /// registered for this context.
201    pub unsafe fn unregister_user_texture_raw(
202        &mut self,
203        texture: &mut crate::texture::TextureData,
204    ) {
205        self.unregister_user_texture_ptr(texture.as_raw_mut());
206    }
207
208    fn unregister_user_texture_ptr(&mut self, texture: *mut sys::ImTextureData) {
209        let _guard = CTX_MUTEX.lock();
210        self.assert_current_context("Context::unregister_user_texture()");
211        assert!(
212            !texture.is_null(),
213            "Context::unregister_user_texture() received a null texture"
214        );
215        if let Some(registration) = take_user_texture_registration(self.raw, texture) {
216            unregister_user_texture_registration(registration);
217        }
218    }
219}
220
221/// RAII token returned by `Context::register_user_texture_token()`.
222///
223/// On drop, this unregisters the corresponding `ImTextureData*` from ImGui's internal user texture
224/// list.
225#[derive(Debug)]
226pub struct RegisteredUserTexture {
227    ctx: *mut sys::ImGuiContext,
228    tex: *mut sys::ImTextureData,
229    alive: Weak<()>,
230}
231
232impl Drop for RegisteredUserTexture {
233    fn drop(&mut self) {
234        if self.ctx.is_null() || self.tex.is_null() || self.alive.upgrade().is_none() {
235            return;
236        }
237
238        let _guard = CTX_MUTEX.lock();
239        if let Some(registration) = take_user_texture_registration(self.ctx, self.tex) {
240            unregister_user_texture_registration(registration);
241        }
242    }
243}