🦊 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:
Visible if (layerMaskCamera & layerMaskComponent) > 0
This allows UI-only cameras, debug passes, and targeted rendering.
5. Lifecycle Phases
5.1 Startup
- Host loads the dynamic library.
- Host calls:
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_tickto 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:
vulfram_dispose();
The core releases windows, GPU resources, and internal allocations.
6. Recommended Host Loop
A practical frame loop looks like this:
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_tickconsumes queued commands. - Events after tick: events are collected during the tick.
7. One-shot Upload Pattern
Uploads are single-use:
vulfram_upload_buffer(buffer_id, type, data)Cmd*Createcommand references thatbuffer_id- 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/