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:

  1. Build-time asset work
  2. Runtime world work
  3. 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:

  • global is the low-level utility layer.
  • core is reusable engine infrastructure.
  • impl is concrete engine content and rendering behavior.
  • pipeline is the cook workflow: dependency_computer, asset_builder, and asset_gatherer.
  • loader is the runtime shell.
  • Testbed is the current project.
  • tests contains regression and smoke coverage.
  • utilities contains 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:

  1. loader initializes engine services.
  2. loader mounts the cooked graphics volume.
  3. loader registers asset codecs.
  4. loader loads shader archive records.
  5. loader builds ProjectRuntimeContext.
  6. Testbed creates an ECS world.
  7. Engine systems load assets and prepare runtime resources.
  8. core/frame drives update and rendering.

During a frame:

  1. platform and input state update
  2. project update callback runs
  3. ECS systems tick
  4. render passes prepare resources
  5. command lists are recorded and submitted through the GPU task scheduler
  6. swapchain presentation finishes the frame
  7. 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.