From eebe29e73a3569376b69ca5923839d96f665f275 Mon Sep 17 00:00:00 2001 From: Melody Madeline Lyons Date: Tue, 2 Jul 2024 09:06:32 -0700 Subject: [PATCH] Store textures in a Weak When a texture is no longer referenced anymore, it'll be deallocated. We still get the optimization of not needing to load the same texture multiple times, however when loading a texture that's not used anywhere else it'll immediately be deallocated. This is useful for graphic pickers, which shouldn't keep textures loaded --- crates/graphics/src/loaders/atlas.rs | 72 +++++++++++++++++++++----- crates/graphics/src/loaders/texture.rs | 13 +++-- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/crates/graphics/src/loaders/atlas.rs b/crates/graphics/src/loaders/atlas.rs index 7507b1b6..2f9741db 100644 --- a/crates/graphics/src/loaders/atlas.rs +++ b/crates/graphics/src/loaders/atlas.rs @@ -14,11 +14,39 @@ // // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crate::{Atlas, GraphicsState}; +use crate::{primitives::tiles::AUTOTILE_AMOUNT, Atlas, GraphicsState, Texture}; +use std::sync::{Arc, Weak}; #[derive(Default)] pub struct Loader { - atlases: dashmap::DashMap, + atlases: dashmap::DashMap, +} + +struct WeakAtlas { + atlas_texture: Weak, + autotile_width: u32, + tileset_height: u32, + autotile_frames: [u32; AUTOTILE_AMOUNT as usize], +} + +impl WeakAtlas { + fn upgrade(&self) -> Option { + self.atlas_texture.upgrade().map(|atlas_texture| Atlas { + atlas_texture, + autotile_width: self.autotile_width, + tileset_height: self.tileset_height, + autotile_frames: self.autotile_frames, + }) + } + + fn from_atlas(atlas: &Atlas) -> Self { + WeakAtlas { + atlas_texture: Arc::downgrade(&atlas.atlas_texture), + autotile_width: atlas.autotile_width, + tileset_height: atlas.tileset_height, + autotile_frames: atlas.autotile_frames, + } + } } impl Loader { @@ -28,11 +56,17 @@ impl Loader { filesystem: &impl luminol_filesystem::FileSystem, tileset: &luminol_data::rpg::Tileset, ) -> color_eyre::Result { - Ok(self - .atlases - .entry(tileset.id) - .or_insert_with(|| Atlas::new(graphics_state, filesystem, tileset)) - .clone()) + self.atlases + .get(&tileset.id) + .as_deref() + .and_then(WeakAtlas::upgrade) + .map(Ok) + .unwrap_or_else(|| { + let atlas = Atlas::new(graphics_state, filesystem, tileset); + let weak_atlas = WeakAtlas::from_atlas(&atlas); + self.atlases.insert(tileset.id, weak_atlas); + Ok(atlas) + }) } pub fn reload_atlas( @@ -41,11 +75,25 @@ impl Loader { filesystem: &impl luminol_filesystem::FileSystem, tileset: &luminol_data::rpg::Tileset, ) -> color_eyre::Result { - Ok(self - .atlases - .entry(tileset.id) - .insert(Atlas::new(graphics_state, filesystem, tileset)) - .clone()) + let atlas = Atlas::new(graphics_state, filesystem, tileset); + let weak_atlas = WeakAtlas::from_atlas(&atlas); + self.atlases.insert(tileset.id, weak_atlas); + Ok(atlas) + } + + pub fn get_atlas(&self, id: usize) -> Option { + self.atlases + .get(&id) + .as_deref() + .and_then(WeakAtlas::upgrade) + } + + pub fn get_expect(&self, id: usize) -> Atlas { + self.atlases + .get(&id) + .as_deref() + .and_then(WeakAtlas::upgrade) + .expect("Atlas not loaded!") } pub fn clear(&self) { diff --git a/crates/graphics/src/loaders/texture.rs b/crates/graphics/src/loaders/texture.rs index e330f4db..2652cfb0 100644 --- a/crates/graphics/src/loaders/texture.rs +++ b/crates/graphics/src/loaders/texture.rs @@ -24,12 +24,12 @@ use dashmap::DashMap; -use std::sync::Arc; +use std::sync::{Arc, Weak}; use wgpu::util::DeviceExt; pub struct Loader { - loaded_textures: DashMap>, + loaded_textures: DashMap>, placeholder_texture: Arc, blank_autotile_texture: Arc, @@ -231,18 +231,21 @@ impl Loader { let texture = register_native_texture(self.render_state.clone(), texture, Some(path.as_str())); - self.loaded_textures.insert(path, texture.clone()); + self.loaded_textures.insert(path, Arc::downgrade(&texture)); texture } pub fn get(&self, path: impl AsRef) -> Option> { - self.loaded_textures.get(path.as_ref()).as_deref().cloned() + self.loaded_textures + .get(path.as_ref()) + .as_deref() + .and_then(Weak::upgrade) } pub fn remove(&self, path: impl AsRef) -> Option> { self.loaded_textures .remove(path.as_ref()) - .map(|(_, value)| value) + .and_then(|(_, value)| value.upgrade()) } pub fn clear(&self) {