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 |
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
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
setEntityStreamScene(...) registers lightweight TileComponent stubs only, parented under the supplied root entity. No geometry is resident yet.
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
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
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.
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
MeshResourceManager.evictUnused() removes zero-ref cached meshes. This is the first stage of geometry relief before more aggressive runtime eviction.
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
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?