SceneDB is layered. The bottom is a paged storage engine and a full archetype ECS; spatial, streaming, and GPU layers sit above it; a compile-time phase machine enforces ordering; and the replication layer rides on top of the frame boundary.
FrameDriver: simulate → harvest → boundary → repeat
Paged SoA storage. 256 rows/page, 64B aligned columns, swap-and-pop compaction at frame boundaries. Handles stay stable; generation counters prevent dangling pointers.
World, an archetype ECS: entities grouped by component set into contiguous columns. An archetype-graph edge cache keeps repeated insert/remove transitions to two Vec reads. Bundle spawn resolves the destination archetype once; queries resolve per-archetype once and iterate rows by pointer arithmetic.
Six dedicated f32 columns for AABB min/max per axis. AABB and frustum queries scan column arrays directly. No per-entity iteration, no allocation. Scalar reference must match SIMD bit-for-bit.
Concentric classification into Outer / Margin / Inner domains with hysteresis bands. The bands damp boundary jitter. Multiple observer AABBs and persistent pins coexist on the same grid.
Region-partitioned SSBOs shared across every registered cell. Delta-sync uploads only dirty rows. Generation buffer + slot mirror in VRAM for GPU-side handle validation, with bulk rebuild for device loss.
Row buffers, texture arrays, and asset registries resolve through one keyed GpuBufferRegistry. DynamicGpuBuffer covers pipeline-owned data (cull outputs, draw batches) with transparent growth; GrowableSceneBuffer backs the World mirror.
A second storage model alongside the paged cells: an archetype ECS (World / Entity / Component) with an opt-in GPU mirror. Build it wired via World::new_with_gpu_mirror and every #[gpu] field auto-registers on first insert and auto-flushes inside step(). Once-mode fields upload on first insert only; DirtyTracked fields batch into one flush per frame. get_mut returns a Mut guard that writes through on drop; scattered churn routes through a GPU scatter-write compute pass.
GPU-side asset storage with suballocation, keyed through the buffer registry: GeometryArena, MeshRegistry, ClusterBuffer, TextureStore, MeshletBuffer, and MaterialRegistry. Each one carries corruption validation and rebuild gates.
Per-view spatial queries with one staging array per view (no shared state), routing hits into mesh-class buckets for indirect draw dispatch.
FrameDriver owns one frame's progression: SimulateA → SimulateB → Harvest → Boundary (retire → compact → sync). Zero-sized witnesses gate every phase. SimulateA and SimulateB are sealed, so invalid transitions are unrepresentable.
SceneDb owns a World, a SubsystemRegistry, and a FrameDriver. step() runs every subsystem's simulate hooks and flushes any attached mirror; step_gpu() drives Harvest → Boundary. Subsystems register once, hook only the phases they need, and stay callable by name from scripts via #[scenedb_subsystem] / #[subsystem_method].
For component patterns where one entity points at another (portals, multi-body attachments), RelationIndex builds a dense columnar view over World. Confirmed-reciprocal pairs only, with unmatched and conflict buffers for the caller to resolve. Rebuilt once per boundary; reads are zero-allocation slices.
The full primitive suite: change tracking, delta encoding, interest management, authority, events/RPCs, snapshots, and client-side prediction. All graphics-free and always available.
A Criterion bench in benches/vs_bevy_ecs.rs runs matched, single-threaded World/Query scenarios head-to-head against bevy_ecs on the exact same component shapes and entity counts.
What gets it there: an archetype-graph edge cache (repeated transitions cost two Vec reads), a WorldQuery init/fetch split (column resolution once per archetype, pointer arithmetic per row), Bundle spawn (one destination archetype, every column written directly), and a zero-allocation column-move path for migration.
1.8×
faster than bevy_ecs: archetype migration (add → add → remove, 10k entities)
parity
spawn with 4 components, 1k–10k entities (within measurement noise)
6–11%
behind bevy_ecs on 2-component query, 1k–50k entities, matched shapes
faster
than bevy_ecs on 4-component query, 10k entities
SceneDB is built around single-writer, shared-reader discipline gated by the phase machine. Within Simulate, systems run in parallel on independent handles. SceneGpuStore::write_transform is &self-safe with interior atomics. Harvest scans are read-only on SpatialCell and safe to run per-view across a job system. The boundary phase is single-threaded.
LivenessMask stores each 64-row word as an AtomicU64 with Relaxed ordering. Set during Simulate (single writer), read during Harvest (concurrent readers with a lease hold). No CAS loops, no SeqCst, no shared-state concurrency inside the storage itself.
1// FrameDriver owns one frame's progression through the phase machine.2//3// driver.begin() → SimulateA (gameplay mutation)4// ↓5// SimulateB (physics writeback; Release fence)6// ↓7// HarvestPhase (read-only; Acquire fence)8// ↓9// BoundaryPhase (retire → compact → sync)10// ↓11// next driver.begin()12//13// SceneDb wraps the same chain: step() = SimulateA → SimulateB plus a14// World-mirror flush; step_gpu() = Harvest → Boundary with your store.
Ready to write your first query?
Read the Docs