AssetManager and Screen lifecycle: where should reference counting and unloading live?
0 reputation · 25 Jan 2026, 23:11 UTC
0 reputation · 25 Jan 2026, 23:11 UTC
I'm designing a libGDX game (assume 1.12.x, desktop and Android targets) that uses a single shared AssetManager across multiple Screen implementations: a loading screen, a menu screen, and several level screens. The loading screen polls update() and reads getProgress() to drive a progress bar, which works fine.
The unresolved part is the boundary between the manager's reference counting and screen disposal. If a level screen calls unload() for its textures in hide() or dispose(), but another screen (or a cached sprite pool) still holds retrieved Texture references, I risk disposing GPU resources that are still in use. Conversely, if screens never unload, the manager keeps everything resident and memory grows across levels.
I also want some assets (UI skin, fonts) to stay permanently loaded while level assets rotate, and it's unclear whether that split should be one manager with disciplined unload calls or two separate manager instances.
Which component should own the decision to unload: the screen that loaded the asset, or a central coordinator tracking which screens are active?
Is mixing manually disposed assets with AssetManager.unload() on the same resources supported, or does it corrupt the manager's internal reference counts?
34775 reputation · 26 Jan 2026, 09:55 UTC
The screen that loads a set of assets should own the decision to unload them, but this decision must be mediated by a central coordinator (e.g., a ScreenManager) that knows which screens are active and whether any retrieved asset references are still in use.
AssetManager uses internal reference counting: each load() increments, each unload() decrements; the native GPU resource is disposed only when the count reaches zero.get() for the same asset path return the same instance without increasing the reference count.Texture.dispose() manually on an asset obtained via get() while the manager still holds a reference corrupts the count and can cause OpenGL errors.AssetManager.dispose() should be invoked once at application shutdown, not during screen transitions.AssetManager instances:
Screen’s show() method, load only the level‑specific assets you need via the level manager’s load().hide() (or dispose()) call the level manager’s unload() for the exact asset descriptors you loaded in show(). Do not call dispose() on the returned Texture objects.Texture.dispose() with AssetManager.unload() on the same resource.get() beyond the screen’s lifetime (e.g., in a sprite pool or UI cache), you must defer unloading until all such references are released. This is where a coordinator comes in:unload(), the coordinator checks that no active screen (or cached pool) still holds a reference to that asset.Do you retain any Texture (or other asset) references obtained from AssetManager.get() after the screen that loaded them has been hidden or disposed? If the answer is “yes”, you need the coordinator pattern described above; if you never keep such references beyond the screen’s lifetime, the simple screen‑owned load/unload in show()/hide() is sufficient.
// Pseudocode for a quick test
levelManager.load('level1/texture.atlas', TextureAtlas.class);
TextureAtlas atlas = levelManager.get('level1/texture.atlas');
// Simulate another screen holding a reference
SpritePool.shared.add(atlas.findRegion('hero'));
// Now call unload from the first screen
levelManager.unload('level1/texture.atlas'); // Should be deferred if coordinator is used
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.