Engine Concepts - limztudio/NWBLot GitHub Wiki
Engine Concepts
This page explains the main parts of NWBLot before going into file-level detail.
The Engine Has Three Kinds Of Work
NWBLot can be understood as three connected workflows:
- Build-time asset work
- Runtime world work
- Runtime rendering work
The build-time workflow turns authored content into stable binary data. The runtime world workflow creates and updates scene state. The runtime rendering workflow turns that scene state into GPU work.
Build-Time Asset Work
The asset workflow starts from authored content:
.nwb / .slang / .slangi / .bind / .surface / .bxdf -> asset pipeline -> cooked volume
The pipeline is responsible for validation. That includes asset metadata, shader compilation, material interface binding, typed material layout, material surface and BXDF source resolution, mesh payload packing, dependency tracking, and deterministic output.
The runtime benefits from that work because it can load known binary formats instead of re-validating every source format during startup.
flowchart LR
Authored["Authored files"]
Cooker["Pipeline validation"]
Binary["Cooked binary records"]
Runtime["Typed runtime loading"]
Authored --> Cooker --> Binary --> Runtime
Runtime World Work
The world workflow starts when the loader creates the project context and calls project callbacks.
loader -> ProjectRuntimeContext -> project callbacks -> ECS world
The ECS world owns scene state. Systems update that state or derive runtime caches from it.
Core ECS only provides the generic entity/component/system mechanism. Engine content such as scene transforms, renderable objects, models, skinning, CSG, and UI lives in implementation modules.
flowchart LR
Loader["Loader"]
Context["ProjectRuntimeContext"]
Project["Project callbacks"]
World["ECS World"]
Systems["Systems tick and prepare state"]
Loader --> Context --> Project --> World --> Systems
Runtime Rendering Work
The renderer workflow starts from ECS state and typed assets:
ECS state + material assets + mesh assets + shader archive -> renderer passes -> selected graphics backend
The graphics provider is selected at compile time through neutral RHI contracts and selected headers. Vulkan is the default and only implemented provider; Metal remains an unsupported future extension. Selection preserves concrete calls without adding runtime dispatch. See Architecture for the include boundary.
The renderer is material-driven. Renderable entities reference material and mesh/model data. Renderer systems resolve those assets, prepare GPU resources, choose feature paths, and record command lists.
The renderer can use different GPU paths based on device features:
- native mesh shaders when supported and enabled
- compute/vertex emulation when native mesh shaders are not active
- hardware ray tracing when acceleration structures, RT pipelines, and ray query support exist
- software BVH shadow traversal when hardware RT is unavailable or not selected for that path
flowchart LR
ECS["ECS state"]
Assets["Typed assets"]
Archive["Shader archive"]
Renderer["Renderer systems"]
Backend["Selected graphics backend"]
Present["Presented frame"]
ECS --> Renderer
Assets --> Renderer
Archive --> Renderer
Renderer --> Backend --> Present
Repository Layers
The source tree has a clear ownership structure:
globalis the low-level utility layer.coreis reusable engine infrastructure.implis concrete engine content and rendering behavior.pipelineis the cook workflow:dependency_computer,asset_builder, andasset_gatherer.loaderis the runtime shell.Testbedis the current project.testscontains regression and smoke coverage.utilitiescontains standalone tools.
The most important split is the core service layer versus impl.
The core service layer should stay generic. It lives under core and owns asset management, ECS mechanics, graphics abstraction, CPU and GPU task scheduling, frame/window control, the IFilesystem volume interface, crash handling, telemetry, and logging.
impl owns concrete engine behavior. It provides asset types, material/shader cooking, scene systems, renderer systems, model/skinning systems, CSG, and UI integration.
Runtime Does Not Go Backward
The intended data direction is one-way:
source assets -> cooked volumes -> typed assets -> ECS state -> renderer state -> GPU
Runtime systems should not silently move backward in that chain. For example:
- runtime graphics should consume cooked SPIR-V bytecode, not compile Slang source
- renderer material binding uses typed cooked metadata and generated typed accessors
- project assets should be loaded through
AssetManager, not by ad hoc filesystem reads - shared shader ABI constants should come from shared headers, not duplicated runtime definitions
This rule keeps the runtime predictable and makes tests able to verify cook output, asset loading, material binding, and renderer behavior independently.
How The Main Parts Fit Together
At startup:
loaderinitializes engine services.loadermounts the cookedgraphicsvolume.loaderregisters asset codecs.loaderloads shader archive records.loaderbuildsProjectRuntimeContext.Testbedcreates an ECS world.- Engine systems load assets and prepare runtime resources.
core/framedrives update and rendering.
During a frame:
- platform and input state update
- project update callback runs
- ECS systems tick
- render passes prepare resources
- command lists are recorded and submitted through the GPU task scheduler
- swapchain presentation finishes the frame
- telemetry and frame graph data are collected when enabled
CPU work during those steps uses the frame-owned CpuTaskScheduler. GPU recording, submission, and device waits use the peer GpuTaskScheduler. See CPU Task Scheduler and GPU Task Graph.
The detailed versions of those two flows are Runtime Boot and Renderer Frame Flow.