🦊 Architecture, Lifecycle, and Main Loop

This document explains how Vulfram is structured, how the core’s lifecycle works, and how the host is expected to drive the main loop.


1. High-Level Architecture

Conceptual flow:

Host → (commands & uploads) → Vulfram Core → WGPU / GPU

Host responsibilities

  • Own game logic and world state.
  • Generate logical IDs for windows, components, and resources.
  • Build MessagePack command batches.
  • Drive the frame loop (vulfram_tick).
  • Consume responses and events from the core.

Core responsibilities

  • Create windows and surfaces (via platform proxies).
  • Manage GPU resources and pipelines (via WGPU).
  • Track components (camera/model/light) and their instances.
  • Process commands, generate events, render frames.

2. Components, Resources, and Instances

  • Components are scene entities (camera/model/light) created with host IDs.
  • Resources are sharable assets (geometry/material/texture) referenced by IDs.
  • Instances are core-only runtime records that back each component.

The host always talks in IDs; the core resolves them to its internal instances.


3. Asynchronous Resource Linking

Vulfram is tolerant to out-of-order creation:

  • Models can reference geometry/material IDs that don’t exist yet.
  • Materials can reference texture IDs that don’t exist yet.

Missing references use fallback resources so rendering continues. When the real resource is created later, the core adopts it automatically.


4. Layer Masks & Visibility

Visibility uses u32 bitmasks:

text
Visible if (layerMaskCamera & layerMaskComponent) > 0

This allows UI-only cameras, debug passes, and targeted rendering.


5. Lifecycle Phases

5.1 Startup

  1. Host loads the dynamic library.
  2. Host calls:
c
vulfram_init();

The core initializes queues, platform proxy, and global state.

5.2 Loading / Initial Setup

Typical sequence:

  • Upload heavy data (meshes, textures).
  • Send create commands for resources and components.
  • Call vulfram_tick to process them.

5.3 Main Loop

During each frame:

  • Host sends updates via command batches.
  • Core processes commands and renders.
  • Host reads responses and events.

5.4 Shutdown

When done:

c
vulfram_dispose();

The core releases windows, GPU resources, and internal allocations.


A practical frame loop looks like this:

text
while (running) {
  1. Update host logic (ECS/scripts)
  2. Upload blobs (optional)
  3. Send command batch
  4. vulfram_tick(time, delta_time)
  5. Receive responses (optional)
  6. Receive events
  7. Receive profiling (optional)
}

Why this order matters

  • Uploads before commands: create commands reference BufferIds.
  • Commands before tick: vulfram_tick consumes queued commands.
  • Events after tick: events are collected during the tick.

7. One-shot Upload Pattern

Uploads are single-use:

  1. vulfram_upload_buffer(buffer_id, type, data)
  2. Cmd*Create command references that buffer_id
  3. Core consumes and releases the upload

Use CmdUploadBufferDiscardAll if you need to clear unused uploads.


8. Render Ordering & Batching

Per camera:

  • Opaque/masked objects: sort by (material_id, geometry_id) to batch.
  • Transparent objects: sort by depth to preserve blending.

9. Where to Look in Code

If you work on the Rust core:

  • Platform proxies: src/core/platforms/
  • Command routing: src/core/cmd.rs
  • Queues and ABI: src/core/queue.rs
  • Render system: src/core/render/
Documentation Vulfram Core