Introduction
Mid Engine is a modular, high-speed Rust engine toolkit — "The Middle Man."
Traditional monolithic engines hide their internals behind layers of abstraction, while others demand unlimited hardware just to render a basic scene. Mid Engine takes a different approach: a modular toolkit for developers who want complete control over their hardware stack, with every crate designed to be dropped into a larger project rather than forcing an all-or-nothing commitment.
It's a true "Middle Man" in two senses. First, every crate is FFI-compatible out of the box, built with as few external dependencies as possible, so it can sit between a game's logic and a lower-level system (or a completely different language's codebase) without friction. Second, it's meant to be the layer between "roll everything yourself" and "adopt a full engine" — whether that means assembling a sovereign engine from these crates, or injecting individual modules into an existing C++/C# environment through raw pointers.
Where this book fits
This is the user-facing documentation site — architecture, how to use each
crate, and the project's own conventions. It's a different thing from the
docs/*.md files in the repository itself, which are internal, AI-facing
working notes (design decisions, fix histories, MSRV walls) meant for
whoever — human or AI — is actively developing the engine, not for someone
using it. If something here seems to assume familiarity a repo-only reader
wouldn't have, that's the split working as intended, not a mistake.
See Tests and Benchmarks for live results from each crate's own CI, and the Getting Started page to build the workspace yourself.
Getting Started
Mid Engine is a Cargo workspace. Most crates build on a plain, current
stable toolchain, but a handful need a newer Rust than the workspace's
usual floor — see Crate Dependency Order and each
crate's own page for specifics before assuming a bare, unqualified command
below will cover everything.
Build and test
# Everything a bare-minimum, no-flags toolchain can build:
cargo build
cargo test
# A release build:
cargo test --release
# One crate specifically -- always safe, regardless of any MSRV wall
# elsewhere in the workspace:
cargo build -p mid-math
cargo test -p mid-math
# The headless server example:
cargo run --example headless-server
A note on -p
Several crates deliberately need a newer toolchain than the rest of the
workspace (an upstream dependency's own edition2024 requirement, mostly —
see each crate's page for the specific wall). A bare cargo build/cargo test with no -p flag pulls every workspace member in, including those.
Building a single crate with -p <crate-name> only resolves that crate's
own dependency closure, so it stays on whatever toolchain that crate
actually needs — usually the workspace floor.
CI
Every crate's tests run through its own workflow_dispatch-only GitHub
Actions workflow — nothing runs automatically on push. Trigger one from the
repository's Actions tab, then check the Tests page (or the run's
own Job Summary) for results. See Contributing & CI for
the full convention.
Design Mandates
Four rules apply to every crate in this workspace, no exceptions:
Multiplayer-first
Network sync is baked into the ECS from day one, rather than being bolted on later. Multiplayer isn't a feature added after the fact — it shapes how state is structured from the start.
FFI-ready
Every crate exposes a strict #[repr(C)] FFI boundary to act as a
cross-language "Middle Man." In practice: rlib + cdylib + staticlib
crate types, and a real extern "C" surface with a C header, not just a
Rust API that happens to be #[repr(C)]-annotated internally.
Zero hidden abstractions
If you need to understand the memory layout, you can read the code directly. No macro-generated indirection that obscures what's actually stored where.
Profile before optimize
Every performance claim cites a real, benchmarked [RELEASE] build number —
not a theoretical estimate. See Benchmarks for the numbers
behind any performance claim made elsewhere in this book.
Zero-to-minimal external dependencies
Every core crate, no exceptions. This project has turned down its own
published dixscript crate as a core dependency over its transitive
dependency count — the bar is real, not aspirational. When a crate needs a
capability an external crate provides (a fast hash algorithm, a spin-based
mutex for no_std), the default is to check whether it can be hand-rolled
first, and to defer the decision with a named trigger condition when it
can't be decided cleanly yet. mid-platform's own page is the clearest
example of this in practice.
Crate Dependency Order
mid-math (no engine deps — pure math foundation)
mid-ptr (no engine deps — type-erased pointer wrappers)
mid-platform (no engine deps — Mutex/Arc/atomics/cells)
mid-collections (no engine deps — SparseSet, generational index, FFI span)
mid-arena (no engine deps — bump/slot/compact arenas)
mid-alloc (no engine deps — custom allocators, SpinLock)
mid-common (uses mid-math — shared traits and error types)
mid-log (uses mid-common)
mid-trace (uses mid-common)
mid-geom (uses mid-math — geometric algorithms)
mid-ecs (uses mid-math, mid-common, mid-collections)
mid-net (uses mid-math, mid-common)
mid-physics (uses mid-math, mid-geom)
mid-anim (uses mid-math, mid-ecs)
The crates with no engine-internal dependency at all (mid-math, mid-ptr,
mid-platform, mid-collections, mid-arena, mid-alloc) are the ones
safest to work on in parallel — nothing else in the workspace has to land
first for one of them to be usable, and nothing about them can be blocked by
another crate's own unfinished state.
See each crate's own page for what it actually depends on today versus this
list's target shape — a couple of these (mid-physics, mid-anim,
mid-app, mid-time) currently exist as early v0 stubs rather than
finished crates.
mid-math
SIMD-dispatched vector and matrix math library: Vec2/Vec3/Vec4,
Quat, Mat3/Mat4, strictly 16-byte-aligned #[repr(C)] primitives for
FFI safety. Includes a hand-rolled MidVec<T, N> small-vector container
(union + MaybeUninit) used by curve types and cascaded shadow maps.
Zero external dependencies, comprehensive benchmarking infrastructure
(see Benchmarks). The second crate in the workspace (after
mid-math itself, chronologically first) to opt into
[lints] workspace = true and take on real unsafe for its SIMD
intrinsics.
Status: practically done, second optimization pass planned.
mid-common
Shared types and traits used across the workspace — EntityId, TickId,
network-sync-related types. Depends on mid-math.
Kept deliberately thin: helper crates (mid-app, mid-time, and others)
live as their own workspace members rather than being folded in here, so
this crate doesn't accumulate every cross-cutting concern by default.
Status: in progress.
mid-log
Non-blocking tiered logger built on a lock-free SPSC ring buffer — zero
frame-time impact on the hot path. Ships a C header for FFI callers
(headers/mid_log.h) and has its own C-side smoke test.
Status: done, needs a second optimization pass.
mid-collections
no_std + alloc collection types: a generational-index allocator, a
sparse set, and FfiSpan — a type-erased, C-safe view into a Rust array for
crossing the FFI boundary safely (built on zerocopy).
FfiSpan specifically is why mid-platform's own Phase 1 doesn't need to
solve the "type-erased pointer for FFI" problem again — see that crate's
own page and docs/roadmap.md's Decision 3 for the full reasoning.
Status: in progress.
mid-arena
Arena allocators: bump, slot, and compact-slot variants, each benchmarked against comparable crates. No engine-internal dependencies.
Status: in progress — next up for updates and proper benches per the project roadmap.
mid-alloc
Custom memory allocators and layout management, including SpinLock<T> — a
hand-rolled spinlock validated under real multi-threaded stress tests (8
real OS threads, thousands of increments each, zero lost updates). That
same proven algorithm is the basis for mid-platform::sync::Mutex's own
no_std fallback (an independent copy, not a dependency — see that crate's
page for why).
Status: in progress; paused pending mid-arena updates.
mid-ptr
Type-erased raw pointer wrappers, ported from Bevy's bevy_ptr crate
(MIT/Apache-2.0) and adapted to this workspace's own conventions. no_std,
zero dependencies.
What it gives you
Ptr,PtrMut,OwningPtr— type-erased pointers mirroring&T,&mut T, andBox<T>respectively, minus the compile-time type information. Useful for storing heterogeneous data (different component types in one column, for instance) without generics at the storage layer.MovingPtr— moves a value to a new location without ever passing it by value, and can be deconstructed into per-fieldMovingPtrs via thedeconstruct_moving_ptr!macro. Useful for migrating a value's bytes between two locations (an archetype table column, say) without an extra copy.ThinSlicePtr— a&[T]with the length stripped out, for callers that already track the length separately.Aligned/Unaligned— marker types threaded through all of the above, so a pointer's alignment guarantee (or lack of one) is visible in its type rather than only in a doc comment.
Full port, including MovingPtr
This is a close port of the real, current upstream source, not a redesign —
including the newer MovingPtr + field-deconstruction machinery, which is
genuinely the more novel, riskier-to-hand-verify half of the crate. Built on
direct instruction, after surfacing that the project's own roadmap had
originally deferred this crate.
FFI
Not yet exposed over a C boundary — a known, tracked gap against this
project's own FFI-ready mandate, not an oversight left undocumented. See the
repository's docs/mid-ptr.md for the current status.
Status
See the Tests page for the latest CI run, or run
mid-ptr — Tests from the Actions tab yourself.
mid-platform
Platform-agnostic primitives, bevy_platform-shaped but hand-rolled and
dependency-free — unlike mid-ptr, this is not a verbatim port. Real
bevy_platform pulls in spin, portable-atomic, foldhash, hashbrown,
and critical-section; none of mid-engine's real targets (native desktop,
wasm32-unknown-unknown) actually need what most of those exist for.
Phase 1 (built)
sync::atomic— a plain re-export ofcore::sync::atomic. Upstream'sportable-atomicfallback only activates on targets lacking a native atomic width; none of this project's targets do.cell::{SyncCell, SyncUnsafeCell}— pure logic, no locking involved.sync::{Mutex, MutexGuard}— a thinstd::sync::Mutexpass-through when thestdfeature (on by default) is enabled, and a self-contained spin-based fallback otherwise — the same algorithmmid-alloc::SpinLockalready validated under real multi-threaded stress tests, kept as an independent copy rather than a dependency so this crate stays workspace-dependency-free.sync::{Arc, Weak}— a plainalloc::syncre-export.
Phase 2 (not built yet)
RwLock, Once/OnceLock, LazyLock, and Barrier — each needs genuinely
new spin-based concurrency design, not just a port of already-proven logic,
so each deserves its own careful pass rather than being rushed in
alongside Phase 1.
Phase 3 (not decided)
A fast hasher (hand-rollable, deserves a benchmarked pass of its own) and a
HashMap/HashSet (the hard one — reimplementing a real hash table is a
project-sized decision on its own, not resolved yet).
Testing both configurations
This crate has real Cargo features (std, default-on; alloc), not just
an unconditional no_std. CI runs it twice: default features exercise the
std passthrough, --no-default-features exercises the actual hand-rolled
fallback — a genuinely different code path default features would never
even compile.
FFI
Not yet exposed over a C boundary — same tracked gap as mid-ptr.
Status
See the Tests page, or run mid-platform — Tests from the
Actions tab yourself.
mid-net
Reliable UDP transport with a hand-rolled wire codec, targeting a 128 Hz
tick rate, plus a DixScript-based transport layer (native via quinn, and a
wasm32 browser variant). Awaiting ECS integration.
Status: in progress.
mid-ecs
Data-oriented Entity Component System using a hybrid Archetype-core /
Sparse-set-shell layout, built on mid-collections. Benchmarked directly
against bevy_ecs (see Benchmarks).
Status: started — archetype core and sparse shell both underway.
mid-geom
Geometry algorithms: BVH construction, Delaunay triangulation, convex hull,
mesh operations. Depends on mid-math.
Status: planned.
mid-trace
Distributed tracing. Depends on mid-common.
Status: planned.
Performance Targets
| System | Frequency | Budget | Status |
|---|---|---|---|
Network tick (mid-net) | 128 Hz | 7.8 ms / tick | Not started |
Physics (mid-ecs) | 60 Hz | 16.6 ms / tick | Not started |
Max entities (mid-ecs) | 100,000+ per core | — | Not started |
Log hot path (mid-log) | 0 µs | zero frame impact | Implemented |
Math primitives (mid-math) | SSE2 | 16-byte aligned Vec3/4/Quat | In progress |
Per the Design Mandates, every claim here should eventually link to a real benchmark build number on the Benchmarks page rather than stand as an unverified target. Until a row does, treat it as a target, not a measured result.
Contributing & CI
Manual CI only
Every test and benchmark workflow in this repository is workflow_dispatch
only. Nothing runs automatically on push, pull request, or a schedule —
tests and benchmarks take real time and shouldn't run on every commit. Run a
workflow from the repository's Actions tab when you actually want its
results.
Each workflow writes a real, structured summary to its own run's Job Summary (parsed from the raw test/bench output, not a raw log dump), and uploads the raw log plus a JSON results file as a build artifact.
Publishing the site
This site itself deploys the same way: Deploy Site
(.github/workflows/deploy-site.yml) is also workflow_dispatch only. It
rebuilds this book, gathers the latest JSON results each crate's own test
workflow produced, and deploys the whole thing to Cloudflare Pages. Running
a crate's test workflow updates that crate's own results; running Deploy Site afterward is what actually publishes them here.
Documentation conventions
- Every source file starts with a
NOTICEheader comment pointing to its crate's owndocs/<crate-name>.mdfile and section there. Inline code comments describe what/how only — no fix history or decision logs inline. - Each crate's
docs/<crate-name>.md(in the repository's top-leveldocs/, not nested per crate) holds per-file sections, a CI/workflow reference section, and a bottom "Fixes and Problems" section. - This book (
web/site/) is the separate, user-facing counterpart to those internal docs — see Introduction for the split.