Skip to content

Commit

Permalink
Implement a free list for egui texture ids, clarify weak handles beha…
Browse files Browse the repository at this point in the history
…viour (#344)
  • Loading branch information
vladbat00 authored Dec 29, 2024
1 parent 69034c6 commit 960c0d6
Showing 1 changed file with 31 additions and 7 deletions.
38 changes: 31 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,8 @@ impl EguiContexts<'_, '_> {
/// Can accept either a strong or a weak handle.
///
/// You may want to pass a weak handle if you control removing texture assets in your
/// application manually and you don't want to bother with cleaning up textures in Egui.
/// application manually and don't want to bother with cleaning up textures in Egui.
/// (The cleanup happens in [`free_egui_textures_system`].)
///
/// You'll want to pass a strong handle if a texture is used only in Egui and there are no
/// handle copies stored anywhere else.
Expand Down Expand Up @@ -587,27 +588,43 @@ impl EguiRenderToImage {
}

/// A resource for storing `bevy_egui` user textures.
#[derive(Clone, bevy_ecs::system::Resource, Default, ExtractResource)]
#[derive(Clone, bevy_ecs::system::Resource, ExtractResource)]
#[cfg(feature = "render")]
pub struct EguiUserTextures {
textures: bevy_utils::HashMap<Handle<Image>, u64>,
last_texture_id: u64,
free_list: Vec<u64>,
}

#[cfg(feature = "render")]
impl Default for EguiUserTextures {
fn default() -> Self {
Self {
textures: bevy_utils::HashMap::new(),
free_list: vec![0],
}
}
}

#[cfg(feature = "render")]
impl EguiUserTextures {
/// Can accept either a strong or a weak handle.
///
/// You may want to pass a weak handle if you control removing texture assets in your
/// application manually and you don't want to bother with cleaning up textures in Egui.
/// application manually and don't want to bother with cleaning up textures in Egui.
/// (The cleanup happens in [`free_egui_textures_system`].)
///
/// You'll want to pass a strong handle if a texture is used only in Egui and there are no
/// handle copies stored anywhere else.
pub fn add_image(&mut self, image: Handle<Image>) -> egui::TextureId {
let id = *self.textures.entry(image.clone()).or_insert_with(|| {
let id = self.last_texture_id;
let id = self
.free_list
.pop()
.expect("free list must contain at least 1 element");
bevy_log::debug!("Add a new image (id: {}, handle: {:?})", id, image);
self.last_texture_id += 1;
if self.free_list.is_empty() {
self.free_list.push(id.checked_add(1).expect("out of ids"));
}
id
});
egui::TextureId::User(id)
Expand All @@ -617,6 +634,9 @@ impl EguiUserTextures {
pub fn remove_image(&mut self, image: &Handle<Image>) -> Option<egui::TextureId> {
let id = self.textures.remove(image);
bevy_log::debug!("Remove image (id: {:?}, handle: {:?})", id, image);
if let Some(id) = id {
self.free_list.push(id);
}
id.map(egui::TextureId::User)
}

Expand Down Expand Up @@ -1078,9 +1098,13 @@ pub fn update_egui_textures_system(
}
}

/// This system is responsible for deleting image assets of freed Egui-managed textures and deleting Egui user textures of removed Bevy image assets.
///
/// If you add textures via [`EguiContexts::add_image`] or [`EguiUserTextures::add_image`] by passing a weak handle,
/// the systems ensures that corresponding Egui textures are cleaned up as well.
#[cfg(feature = "render")]
#[allow(clippy::type_complexity)]
fn free_egui_textures_system(
pub fn free_egui_textures_system(
mut egui_user_textures: ResMut<EguiUserTextures>,
mut egui_render_output: Query<
(Entity, &mut EguiRenderOutput),
Expand Down

0 comments on commit 960c0d6

Please sign in to comment.