Textures & the Texture Cache
Nightshade manages GPU textures through a centralized TextureCache with generational indexing and reference counting. Textures can be loaded synchronously from bytes, asynchronously from disk or HTTP, or generated procedurally at runtime.
Texture cache
The TextureCache stores every loaded texture as a TextureEntry (a wgpu texture plus its view plus its sampler) in a GenerationalRegistry. Each texture is identified by a TextureId containing an index and a generation counter. Stale references are detected because the generation increments when a slot is recycled, so an old TextureId will not match the slot's current contents.
Loading textures
The most common way to load a texture is through WorldCommand::LoadTexture.
#![allow(unused)] fn main() { world.queue_command(WorldCommand::LoadTexture { name: "my_texture".to_string(), rgba_data: image_bytes, width: 512, height: 512, }); }
The renderer drains the command queue, uploads the RGBA data to the GPU, and stores the texture in the cache under the given name.
Procedural textures
The engine provides three built-in procedural textures loaded at startup via load_procedural_textures().
#![allow(unused)] fn main() { load_procedural_textures(world); }
| Name | Description |
|---|---|
"checkerboard" | Black and white checkerboard pattern |
"gradient" | Horizontal gradient |
"uv_test" | UV coordinate visualization |
Looking up textures
Find a loaded texture by name.
#![allow(unused)] fn main() { let texture_id = texture_cache_lookup_id(&cache, "my_texture"); }
Reference counting
Textures use reference counting for lifecycle management. The cache will not free a texture until every holder has released it.
#![allow(unused)] fn main() { texture_cache_add_reference(&mut cache, "my_texture"); texture_cache_remove_reference(&mut cache, "my_texture"); texture_cache_remove_unused(&mut cache); }
When a texture's reference count reaches zero, texture_cache_remove_unused() frees it.
Dummy textures
If a texture is missing, texture_cache_ensure_dummy() creates a 64x64 purple-and-black checkerboard placeholder. The fallback keeps the renderer from erroring out and makes missing assets obvious in the viewport.
Async texture loading
For loading textures without blocking the main thread, use the TextureLoadQueue system.
Setup
#![allow(unused)] fn main() { use nightshade::ecs::texture_loader::*; struct MyState { queue: SharedTextureQueue, loading_state: AssetLoadingState, } fn initialize(&mut self, world: &mut World) { self.queue = create_shared_queue(); queue_texture_from_path(&self.queue, "assets/textures/albedo.png"); queue_texture_from_path(&self.queue, "assets/textures/normal.png"); self.loading_state = AssetLoadingState::new(2); } }
Processing each frame
#![allow(unused)] fn main() { fn run_systems(&mut self, world: &mut World) { let status = process_and_load_textures( &self.queue, world, &mut self.loading_state, 4, ); if status == AssetLoadingStatus::Complete { // All textures loaded } } }
Loading progress
Track progress for a loading screen.
#![allow(unused)] fn main() { let progress = self.loading_state.progress(); // 0.0 to 1.0 let is_done = self.loading_state.is_complete(); let loaded = self.loading_state.loaded_textures; let failed = self.loading_state.failed_textures; }
Platform behavior
| Platform | Loading Method |
|---|---|
| Desktop | Synchronous file read from disk |
| WASM | Async HTTP fetch via ehttp |
Asset search paths
Configure the directories where texture files are searched.
#![allow(unused)] fn main() { set_asset_search_paths(vec![ "assets/".to_string(), "content/textures/".to_string(), ]); queue_texture_from_path(&queue, "player.png"); // Searches: assets/player.png, content/textures/player.png }
Material textures
PBR materials reference textures by name through MaterialRef.
#![allow(unused)] fn main() { let material = Material { base_texture: Some("albedo".to_string()), normal_texture: Some("normal_map".to_string()), metallic_roughness_texture: Some("metallic_roughness".to_string()), ..Default::default() }; }
See Materials for the full PBR material workflow.