🦊 Overview
Vulfram is a rendering and systems core written in Rust and exposed as a dynamic library. It is designed to be driven by an external host via C-ABI + MessagePack.
Typical hosts:
- Node.js (N-API)
- Lua (via
mlua) - Python (via
PyO3) - Any environment capable of calling C-ABI functions
- Browser runtimes via WASM + WebGPU
The core is intentionally a black box:
You control it only through a small set of
vulfram_*functions and MessagePack command batches.
1. Mental Model
Think of Vulfram as a GPU/renderer appliance:
- The host owns game logic, IDs, and the frame loop.
- The core owns windows, input, GPU resources, and the render pipeline.
This separation keeps the public API small and lets you embed Vulfram anywhere.
2. What the Host Must Do
At a minimum, the host must:
- Generate logical IDs (window, camera, model, material, texture, buffer).
- Build command batches (MessagePack structures).
- Send commands with
vulfram_send_queue. - Drive frames using
vulfram_tick(time, delta_time). - Poll responses and events (
vulfram_receive_queue,vulfram_receive_events).
The host never manages GPU objects, windowing, or input directly.
3. Components vs Resources
Vulfram uses two fundamental categories:
Components
Components are scene participation:
- Camera (view/projection, render order, layer mask)
- Model (transform + references to resources)
- Light (directional, point, spot)
Components are created/updated via commands and always use host-chosen IDs.
Resources
Resources are reusable assets:
- Geometry (vertex/index buffers)
- Materials (surface/shader configuration)
- Textures (image data, samplers)
Resources are also referenced by host-chosen IDs and can be shared across components.
4. IDs, Handles, and Fallbacks
- Logical IDs are integers the host owns.
- Handles are internal core references (not exposed).
Resources can be created out of order:
- A model may reference a geometry/material that does not exist yet.
- A material may reference a texture that does not exist yet.
When that happens, the core uses safe fallback resources so rendering continues. When the real resource appears with the same ID, it is picked up automatically.
5. One-shot Uploads
Heavy binary data is sent through vulfram_upload_buffer:
- Upload bytes with a
BufferId. - Send a
Create*command that references thatBufferId. - The core consumes the buffer once and frees it.
This keeps memory predictable and works well for streaming or incremental loading.
6. Visibility Layers
Visibility is controlled by a u32 bitmask:
layerMaskCameraon cameraslayerMaskComponenton models
A model is visible if:
(layerMaskCamera & layerMaskComponent) > 0
This enables UI/world separation, debug passes, and multi-camera setups.
7. Render Ordering & Batching
Per camera:
- Opaque/masked objects are sorted by
(material_id, geometry_id)to reduce state changes. - Transparent objects are sorted by depth to preserve blending order.
8. Where to Go Next
- Building a binding → ABI Reference and Architecture
- Understanding core internals → Internal API
- Terminology reference → Glossary