Streaming Cache Lifecycle
This document describes how the current streaming architecture manages geometry across three layers:
| Layer | Owns |
|---|---|
GeometryStreamingSystem |
Distance-based residency decisions |
ProgressiveAssetLoader |
Warm CPU RuntimeAssetNode state for tile-owned OCC assets |
MeshResourceManager |
Shared GPU mesh cache for immediate/cache-backed .untold paths |
Two Residency Modes
Full-load / cache-backed meshes
For eager loads, MeshResourceManager owns the shared mesh data:
loadMesh(url:meshName:)returns cached or freshly loaded meshesretain(...)increments residency ownership for an entityrelease(entityId:)decrements the reference countevictUnused()frees GPU data when the ref count reaches zero
Tile-owned OOC meshes
For large streamed tiles, ProgressiveAssetLoader owns the CPU source data:
CPURuntimeEntrystores the parsedRuntimeAssetNodeGeometryStreamingSystemuploads those buffers on demand- eviction normally drops only GPU residency
- tile teardown releases the CPU entries for that root
Current Lifecycle
1. Tiled scene registration
setEntityStreamScene(...) registers lightweight TileComponent stubs only, parented under the supplied root entity. No geometry is resident yet.
2. Tile parse
When a tile enters prefetch range, loadTile(entityId:) parses the tile payload.
At that point the runtime chooses one of two outcomes:
- full-load tile: render entities are GPU-resident immediately
- OCC tile: child
StreamingComponentstubs are registered and backed byCPURuntimeEntry
3. OOC upload
For OOC stubs, GeometryStreamingSystem.loadMesh(...):
- checks tile ownership
- reserves an active streaming slot
- uploads from
ProgressiveAssetLoader - marks the entity loaded and emits residency change events
4. Full-load cache use
For eager/disk-backed meshes, the load path uses MeshResourceManager:
loadMesh(...)retain(...)- entity-local
copyWithNewUniformBuffers() - apply to
RenderComponent
The copied uniform buffers are per-entity, but the underlying cached geometry is shared.
5. Eviction
When geometry leaves range or memory pressure rises:
unloadMesh(...)clears the entity's live mesh referenceMeshResourceManager.release(entityId:)decrements shared cache refs when applicable- OCC stubs keep their CPU source warm until tile/root teardown
- if OCC eviction cannot clear geometry pressure,
evictTileGeometry(...)can unload full-load tile geometry, HLODs, and per-tile LODs outside their protected streaming band
6. Cache cleanup
MeshResourceManager.evictUnused() removes zero-ref cached meshes. This is the first stage of geometry relief before more aggressive runtime eviction.
Why the Split Exists
The split cache model supports both fast reuse and bounded memory:
MeshResourceManageris efficient for shared eager meshes and disk-backed reloadsProgressiveAssetLoaderavoids reparsing large tiles on every near/far traversalGeometryStreamingSystemarbitrates both with the same distance, frustum, and budget logic
Practical Reading
When reviewing a streamed tile today, think of residency in this order:
- Is the tile stub in range?
- Did the tile classify as full-load or OOC?
- If OOC, does the child stub have a
CPURuntimeEntry? - Is the GPU copy currently resident?
- Is batching representing the entity directly or via a cell artifact?
- If geometry pressure is high, is the candidate an OCC stub handled by
evictLRU, or tile-owned full/LOD/HLOD geometry handled byevictTileGeometry?