-
-
Notifications
You must be signed in to change notification settings - Fork 32
Description
This issue described the architecture for Asset is Everything. Tasks will be created in the PR.
Overview
Traditional ECS is boring, anyways this started more of an idea while I was high one day. Now idk what this will lead to but I'll be explaining my intuition and final design goals, idk how good will this design be and if it can be better than others. Here we go:
Unlike having separate disk-time asset and runtime entity, in this design "everything in an asset". Runtime and disk time both call them assets. They are basically having same representation on disk vs runtime. Keep metadata and compressed data together + no components, assets reference other assets and we build a scene graphs of UUID refs that maps to linear pools of different asset types.
Some rules
- SceneGraph maintains the hierarchy relationship between different asset nodes.
- Now how nodes can be tagged is decided by a ISceneGraphRule.
- Only script and transform assets can exist as first level nodes in the scene graph.
- You get the idea, a mesh asset should be child of transform asset, and material will be child of mesh asset.
- All ref asset are child nodes.
- All assets stored in linear pools (MeshPool, TexturePool, MaterialPool, …).
- Uses a freelist internally to track what is free, no deferred references unlike Gfx resource so no need to track generation as we cannot have stale assets.
- References use rz_asset_handle (hader _ payload inedx)
- Child assets grouped together in child slabs to improve traversal locality.
- Assets with hierarchy level are grouped together in memory, maybe use a RB tree with memory addressed cached? not sure will be thought out during implementation.
- Textures & Meshes compressed to BCn/custom format --> metadata will store this info
Misc
- scene graph parsing code in RAW ASM! only rules in C++.
- Job system to load assets, placeholder defaults until then, CBs to refresh loads.
- Models are broken/contructed by scene graph inside blender.
- no House renderer until V.0.8.0.Dev
- write a doc for blender workflow.
- RZthread for multi-threaded asset loading.
[WIP] Design
Asset System, Database & Pools - 1W
- Everything in the engine is an asset. Runtime and on-disk representation use the exact same memory layout with no import-time conversion nonsense.
- There is a central RZAsset pool to manage metadata.
- Actual asset data (payloads) are stored in separate, type-specific RZAssetPools which manage the memory and lifecycle of the assets.
- The handle can easily refer to these pools on a need-to-basis.
- Each defined asset is bidirectionally linked with RZAsset via rz_asset_handle.
- This indirection facilitates easy management of assets and their data without enforcing inheritance or template-based designs.
The rz_asset_handle is a 64-bit value designed for efficient lookups:
- Lower 32-bits: Represents the hot/cold data index in the central asset pool.
- Higher 32-bits: Represents the asset payload data index in the specific asset pool for the actual asset data.
Similar to how RZResourceManager handles resources, the Asset System splits data for cache efficiency. The entirely of RZAsset fits in a single cache line (with still 8 bytes left in hot data for future use).
We maintain a central RZAsset pool specifically to manage high-frequency metadata (UUIDs, reference counts, flags). Each actual asset starts with define that is a rz_asset_handle. So each asset type has it's own pool and same goes for RZAsset as well, link data via handles but keep similar data together. This is the base for this data-oriented design.
- Core Design
- Define Assets
- Create RZAssetPool and RZAssetHeaderPool
- Basic freelist asset pool
- TransformAssetPool specialization
- RZAssetDB for registry of pool, asset types
- Implement [Draft-1] binary serialization #471
- Link to serialization ==> binary save/load tests
- Hookup with RZAssetDB --> write to a stupid *.pak file and as for transforms, write them to a stupid transforms.pak file --> idk let's see how the scenegraph s-expression parser will ref this, maybe file path
- Test RZAssetDB serialization with RZAsset* type formats
Formats, Compression and Asset Types - 1W
- Assets can be CPU-backed (physics, animation), GPU-backed (buffers that never leave VRAM), or pure runtime-only (procedurally generated stuff).
- Optional LZ4 compression on disk. In editor/debug builds we just bypass it entirely – zero decompress cost when iterating.
- Meshes, textures, materials, scripts, audio clips, animation graphs… all treated exactly the same by the loader. One code path, zero special cases.
Asset Loading & Job System - 1W
- Fixed set of pinned, work-stealing worker threads handle the whole pipeline: disk IO → decompress → post-process → GPU upload.
- Asset state is a single atomic u8: NOT_LOADED → LOADING → READY. Anything that needs the asset just spins or registers a callback/fence.
- Deferred loading is default. Scene starts rendering immediately with gray/placeholder materials. Real assets pop in the moment they’re ready. No “loading screen” stalls.
Scene Graph and Traversal - 1W
- Main update traversal is breadth-first and written in hand-tuned ASM for the hot parts. Matrix concat (local × parent) is 100% SIMD.
- SceneNode is literally just a tiny C header + ChildRange with start/count offsets into the global slab.
- Each parent node has a child slab, which is a contiguous array of handles or indices to its immediate children.
When updating transforms, you process all children of a parent in a tight loop:
for parent in depth_level:
parent_matrix = parent.world
children = parent.child_slab // N_batch ≈ 512
for i = 0; i < N_batch; i += simd_unroll: // simd_unroll = 8 floats
load 8 floats from child[i].local_matrix
multiply SIMD(parent_matrix)
store result to child[i].world_matrix
Scene Graph serialization - S-EXPRESSIONS! with transform data! pack
// TODO: in-memory updates/callbacks and memory mapped loading
Scene Graph Rules - 1W
- Strict parenting rules at root level: only Transform and Script components allowed directly under the scene root.
- Mesh instances must sit under a Transform, materials under meshes, textures under materials, etc.
- Rules are enforced with
ISceneGraphRuleinterfaces at edit time and load time. Invalid graphs just refuse to load instead of exploding later. - Because everything is typed handles, dereferencing children is basically a couple of adds and a bounds check – blazing fast.
Blender ↔ Game Integration - 1W
- Full scene graphs + asset references export straight out of Blender into our binary format. No intermediate JSON/XML garbage.
- GPU buffers get allocated lazily after load finishes. While waiting you get instant preview with placeholder materials.
- Editor builds completely disable compression and streaming. Change a texture in Blender → hit save → see it in game within a frame. Actual fast iteration for once.