Asset Flow - limztudio/NWBLot GitHub Wiki
This page explains how an NWBLot asset moves from a source file to runtime rendering.
authored file -> cook entry -> binary payload -> cooked volume -> AssetManager -> typed asset -> ECS/render system
The runtime does not treat assets as loose source files. It expects cooked data with a known asset type and a codec that can load that type.
Source content is edited under engine and project asset roots.
Shared reusable engine assets live under impl/assets. These include common graphics shader code, renderer support shaders, CSG shape assets, and scene/mesh shader ABI helpers. Material interfaces, surfaces, and BXDF policy live under the project asset root that authors them.
Project-specific authored assets live under project trees such as CoolStuff/Testbed/assets. These include Testbed materials, meshes, and project shader files.
Common source asset forms include:
-
.nwbmetadata files -
.texraw UASTC texture sidecars -
.slangshader source -
.slangishader include files -
.bindtyped shader/material interface declarations -
.surfacematerial surface hooks -
.bxdfmaterial lighting BXDF fragments - mesh/model/skin/skeleton source metadata
- CSG shape metadata and evaluator shader includes
Asset types provide cook logic through engine modules. Cook registration connects an authored asset type to code that knows how to parse, validate, compile, and serialize it.
Concrete asset modules register codec factories and cook-entry factories through the core auto-registration helpers. The loader collects codec factories into its AssetRegistry. asset_builder collects CookEntryAutoRegistrar entries into CookEntryRegistry and graphics prepare steps through AssetVolumePrepareAutoRegistrar. Factory collection is deduplicated, so a linked module contributes each factory once.
Examples of concrete cook ownership:
-
impl/assets_shadercooks shader assets through the graphics volume-prepare path -
impl/assets_materialcooks material assets and material binding metadata -
impl/assets_texturecooks typed UASTC texture assets - mesh, skeleton, model, and CSG implementation modules cook their own runtime payloads (the
skinasset is cooked insideimpl/assets_mesh, not a separateassets_skinmodule) -
pipeline/orchestrates discovery, selected-input building, and volume publishing
core/assets owns the generic cook plumbing: cook-entry registration, input lists, asset-bunch expansion, and staged volume publication. Implementation modules retain each asset type's parsing, validation, compilation, and serialization rules.
The pipeline is intentionally the place where expensive or fragile validation belongs.
Cookers read authored metadata and validate the current asset contract. Authored .nwb files expose content and choices an author can control; versions, revisions, fixed payload layouts, derived transport counts, backend compiler profiles, and fixed engine transport switches remain internal. Unknown or retired fields fail validation rather than selecting a compatibility reader.
Validation examples:
- metadata fields must match the asset schema
- shader stages and variants must be named consistently
- shader metadata must declare a concrete physical-stage type, such as
compute_shaderorpixel_shader; genericshaderdeclarations and authoredstagefields are rejected - material metadata must use the fields defined by the material metadata header
-
.bindfiles must declare typed blocks and instances correctly - material
surfaceandbxdfpaths must resolve through asset roots - material and shader interface names must match
- authored material parameter values must match their typed layout fields
- material
texture2dandsamplerfields must usematerial_constantstorage, declare no field attributes, and receive anengine/orproject/typed asset path through their block-scopedasset.parametersentry - texture metadata selects UASTC LDR or HDR,
resolution, andalpha_modefor its implicit same-basename.texsidecar; the cooker derives the complete mip layout, and a variable HDR alpha stream must mirror RGB - shader dependencies must resolve through stable asset roots
For material binding, the current path is typed-only at binding 6. Generated typed .bind helpers are part of the contract. Shaders that include a generated material bind receive NWB_MATERIAL_TYPED_BINDING == 1; in the generated material path this is the pixel shader, while the shared mesh shader stays interface-free.
After validation, cookers write binary payload data.
Shader cooking compiles shader-language sources into SPIR-V bytecode and records shader archive information. Runtime graphics consumes that cooked bytecode.
Material cooking packs:
- material metadata
- shader and variant references
- material interface identity
- material shading-model id from its
bxdf - material surface dispatch id from its
surface - material refractive-caster classification
- typed block layout
- packed parameter bytes
- typed texture and sampler asset references, their block/field identity, resource kind/source, asset-name hash, and constant-byte offset
- stable instance identifiers
Transparent or refractive materials require a project surface hook; explicit shaders are allowed only for opaque, non-refractive materials. When a material declares surface, the cook generates a G-buffer pixel shader from the engine pixel-shader authoring header, the material .bind, and the .surface hook. It then assigns that generated pixel shader plus the shared engine mesh shader as the material's stage shaders.
For transparent surface-authored materials, the cook also generates AVBOIT occupancy, extinction, and accumulate pixel shaders from the same .bind and .surface pair. Shadow paths use the cooked surfaceDispatchId to dispatch the surface hook for transparent occluder optical parameters, and caustic paths use the refractive material flag plus .surface refraction values to identify and trace refractive casters.
Mesh/model/skinning cooking prepares renderer-usable geometry and animation data, including meshlet payloads, model hierarchy data, skin data, and skeleton pose data.
Texture cooking derives the complete mip and alpha layout from the resolution, validates the metadata/implicit-sidecar pair without conversion or re-encoding, and packs the raw linear UASTC blocks, complete mip layout, payload format, alpha mode, and dimension into the typed Texture payload. Every runtime texture uses linear RGB and linear alpha coverage. HDR payloads retain either an opaque/constant alpha declaration or a trailing matching UASTC LDR mask stream. The runtime loader can then create a 2D, cube, or 3D image from the mounted volume without rereading authoring files.
asset_builder writes standalone .nwba artifacts. asset_gatherer then writes the graphics volume through IFilesystem. Publishing is staged so incomplete output is not promoted as a finished runtime volume. Shared volume writing, cook-cache, .nwba artifacts, and pack-manifest support live in core/assets/volume; registered prepare steps gather the per-type payloads that the builder serializes.
At runtime, the loader mounts the graphics volume from the res directory through CreateFilesystem. The mounted IFilesystem becomes the binary source for asset loading.
The handoff from authored source to runtime asset loading looks like this:
flowchart TD
Authored["Authored asset roots<br/>impl/assets<br/>CoolStuff/Testbed/assets"]
Discover["pipeline/launch.py discovers source assets"]
Builder["asset_builder validates by asset type"]
Artifacts["Standalone .nwba artifacts"]
Volume["asset_gatherer publishes graphics volume"]
Mount["Loader mounts res/graphics"]
Source["IAssetBinarySource reads IFilesystem bytes"]
Registry["AssetRegistry selects codec"]
Runtime["AssetManager returns typed asset"]
Systems["ECS and renderer systems consume asset"]
Authored --> Discover --> Builder --> Artifacts --> Volume --> Mount --> Source --> Registry --> Runtime --> Systems
Runtime asset loading uses three main pieces:
-
AssetManagerreceives a virtual path and requested type. -
IAssetBinarySourcereturns bytes from the mounted cooked volume. -
AssetRegistryselects the registered codec for the asset type.
The codec deserializes the binary payload into a typed runtime asset object.
This keeps runtime loading explicit. A material request produces a material asset; a compute shader request produces ComputeShader, and a pixel shader request produces PixelShader. Concrete shader codecs share common IShader storage and one payload layout, but validate the exact SPIR-V entry point and physical stage before publishing state. A wrong-stage or malformed payload fails without replacing previously loaded storage.
Project and engine systems store typed asset references in components and runtime caches.
Typical examples:
- model components reference model assets
- render components reference mesh/material assets
- skinning systems reference skin and skeleton assets
- renderer systems resolve material shader variants through the shader archive
- material preparation resolves cached cooked 2D texture assets into global sampled-image heap slots
- CSG systems reference shape assets and evaluator shader support
Renderer systems then upload or bind the GPU resources required for the current scene.
This flow makes the engine easier to reason about:
- source validation happens before runtime
- runtime asset loading is typed
- shader compilation stays in cook targets
- material layouts are deterministic
- project content and shared engine content stay separated
- cook determinism can be tested independently from rendering
The detailed reference pages are Asset Pipeline, Asset Types, Cooked Volumes, and Shader and Material System.