Expand description
Authored-reference → resolved-handle pump.
A PreExtract data system that turns each entity’s authored asset
references into the runtime handles the GPU projection consumes. It covers
two reference kinds with one collect-then-mutate pass each:
Materials — [MaterialRef] → [MaterialHandle]
(HandleComponent<Box<dyn Material>>):
- [
MaterialRef::Inline] embeds the material value — it is wrapped into a handle keyed by a content-derived UUID, so two entities holding the same inline material share one resolved handle (and oneGpuMaterial). - [
MaterialRef::Asset] references a.kmatin the VFS — it is loaded via theAssetServiceand the resulting handle is keyed by the asset’s stable UUID.
Meshes — [MeshRef] → HandleComponent<Mesh>:
- [
MeshRef::Procedural] carries primitive params — the geometry is rebuilt via [reconstruct_procedural_mesh] and keyed by a content-derived UUID, so identical procedural meshes dedup to one resolved handle (and oneGpuMesh). - [
MeshRef::Asset] references an imported mesh (glTF, OBJ) in the VFS — it is loaded via theAssetServiceand keyed by the asset’s stable UUID.
Each resolved CPU value is also inserted into the matching shared
[AssetStore] sub-store (store::<Box<dyn Material>>() /
store::<Mesh>()) so the GPU projection and future sharing see it. This
system runs before both gpu_material_sync and gpu_mesh_sync, so a
freshly-spawned entity resolves and projects in the same tick.
The resolver is the sole authority that keeps resolved handles consistent
with their authored references. Each authored ref carries an identity
UUID (content-derived for Inline/Procedural, the stable asset UUID for
Asset); every tick the resolver compares it against the resolved handle’s
UUID. In steady state this is a single cheap compare — nothing is loaded,
cloned, or hashed. When the comparison fails (the developer/editor changed
the ref, or no handle exists yet) the resolver (re)resolves, replaces the
stale CPU handle, and drops the stale HandleComponent<GpuMaterial> /
HandleComponent<GpuMesh> so the GPU projection re-projects under the new
identity. This single read-phase compare covers editor inspector edits,
.kmat assignment, save-as, and runtime gameplay mutation alike — there is
no per-mutation-site invalidation.
khora-data never depends on khora-io; this resolver lives here because
it calls the khora-io-owned AssetService while mutating the
khora-data World + AssetStore.