Add Box3D upstream research notes for the testbed
Findings from the engine evaluation that led to this project, kept separate from the table's own A/B results: - Apple Silicon threading: Box3D gets SLOWER past the performance-core count on the M3 Ultra (30 workers 5158ms -> 32 workers 5646ms, 24P+8E). A physics step is a barrier, so a share of the work on a third-speed E-core makes the whole step wait. Read hw.perflevel0.physicalcpu, never hw.ncpu. Applies to any threaded physics on this fleet, not just Box3D. - The benchmark harness: the CMake option is BOX3D_BENCHMARKS (plural), the scene/flag list, upstream's M2 Air + AMD baselines, and a warning that a full sweep runs over an hour on 32 cores — always pin -b and -w. - What the binding does NOT implement (Generic6DOF, ConeTwist, SoftBody3D, per-pair exceptions) plus the silent Area3D-vs-trimesh divergence. - Why pinball and not the ragdoll game: ~/Documents/alright is the better fit but is BLOCKED — its ragdoll is built from Generic6DOFJoint3D. Watch that upstream. - Provenance: Box3D is serious (Catto, MIT, s&box + Esoterica adoption); the binding is five weeks old with zero releases and self-declares experimental. This is a testbed, not a bet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2ca67bb28b
commit
804e4c0fb8
130
godot/BOX3D_ENGINE_NOTES.md
Normal file
130
godot/BOX3D_ENGINE_NOTES.md
Normal file
@ -0,0 +1,130 @@
|
||||
# Box3D — upstream research notes
|
||||
|
||||
*Findings from the evaluation session that led to this testbed existing (2026-08-06, M3 Ultra).
|
||||
Complements [README.md](README.md), which covers this project's own A/B probe results. Nothing
|
||||
here is about the table — it's about the engine underneath it and the hardware it runs on.*
|
||||
|
||||
## 1. Cap physics workers at 24 on this machine, not 32
|
||||
|
||||
The single most actionable finding, and it applies to **any** threaded physics on this fleet —
|
||||
Box3D, Jolt, or anything else.
|
||||
|
||||
The M3 Ultra is **24 performance + 8 efficiency cores**. Box3D's own benchmark, swept across
|
||||
worker counts on this machine, gets *slower* once work spills onto the E-cores:
|
||||
|
||||
| workers | ms | |
|
||||
|---|---|---|
|
||||
| 30 | 5,158 | |
|
||||
| 31 | 5,310 | |
|
||||
| 32 | 5,646 | **10% worse than 30** |
|
||||
|
||||
A physics step is a barrier — every worker waits for the slowest one — so handing a share of the
|
||||
work to a core that runs at roughly a third the speed makes the whole step wait on it. More
|
||||
threads is not more throughput past the performance-core count.
|
||||
|
||||
**So:** don't set worker count from `OS.get_processor_count()` / `hw.ncpu` on Apple Silicon.
|
||||
Read the performance-core count instead:
|
||||
|
||||
```sh
|
||||
sysctl -n hw.perflevel0.physicalcpu # 24 on this box — use this
|
||||
sysctl -n hw.ncpu # 32 — do NOT use this
|
||||
```
|
||||
|
||||
Within the performance cores the threading scales well: upstream's M2 Air `joint_grid` figures
|
||||
are 1,089 ms single-threaded → 309 ms on 4 threads (**3.52×**).
|
||||
|
||||
Caveat on the table above: it is one scene (8,002 bodies / 40,444 contacts), best-of-4 runs, and
|
||||
the low-thread-count rows of that particular sweep were lost to a truncated capture. The
|
||||
*direction* is unambiguous and reproduces; treat the exact millisecond figures as indicative.
|
||||
A pinball table is ~1 ball and a few dozen bodies, so none of this is a bottleneck **here** — it
|
||||
matters when this testbed is used to answer questions for the bigger physics games.
|
||||
|
||||
## 2. The benchmark harness, and how to re-run it
|
||||
|
||||
Box3D ships a real benchmark suite. It is not built by default — note the option is
|
||||
`BOX3D_BENCHMARKS`, **plural** (an easy 20 minutes to lose):
|
||||
|
||||
```sh
|
||||
git clone --depth 1 https://github.com/erincatto/box3d && cd box3d
|
||||
cmake --preset macos -DBOX3D_BENCHMARKS=ON
|
||||
cmake --build --preset macos-release --target benchmark -j 24
|
||||
./build/bin/Release/benchmark --list
|
||||
```
|
||||
|
||||
Flags: `-b <index|name>` one scene · `-w <n>` a single worker count · `-t <n>` max threads ·
|
||||
`-r <n>` repeats (default 4) · `-nc` disable continuous collision · `-s` record step times.
|
||||
|
||||
Scenes: `convex_pile`, `joint_grid`, `junkyard`, `large_pyramid`, `large_world`, `many_pyramids`,
|
||||
`rain`, `trees100/50/25`, `washer`. Upstream keeps reference results in `benchmark/m2air_neon/`
|
||||
(Apple Silicon) and `benchmark/amd7950x_*` — useful baselines to compare a run against.
|
||||
|
||||
**Budget the time.** A full sweep is ~11 scenes × every worker count × 4 repeats and runs for
|
||||
well over an hour on 32 cores. Always pin `-b` and `-w`, and capture with `tee`, not `tail`.
|
||||
|
||||
The library itself builds clean on Apple Silicon (`cmake --preset macos`) and its whole test
|
||||
suite passes in **0.61 s** — a fast, honest smoke test after any upstream bump.
|
||||
|
||||
## 3. What godot-box3d does NOT support
|
||||
|
||||
From the binding's own README. Hitting one of these looks like an engine bug and isn't:
|
||||
|
||||
- **Generic6DOF and ConeTwist joints** — not implemented
|
||||
- **`SoftBody3D`** — not implemented
|
||||
- Separation ray shapes
|
||||
- Per-pair collision exceptions (use layers/masks)
|
||||
- Changing a `PinJoint3D` anchor after creation — recreate the joint instead
|
||||
|
||||
And one behavioural divergence that will bite silently rather than error:
|
||||
|
||||
> **`Area3D` does not detect trimesh or heightmap bodies.** In Box3D a concave or heightmap shape
|
||||
> can never be a sensor *visitor*, by design. No `body_entered` / `body_exited` fires for such a
|
||||
> body. Godot's built-in physics and Jolt both report it. **Give anything that must be
|
||||
> area-detected a convex shape.**
|
||||
|
||||
Pinball is unaffected — it needs hinge joints (supported) and CCD (Box3D's strength), and none of
|
||||
the above. That is exactly why this became the testbed game.
|
||||
|
||||
## 4. Why pinball, and not the ragdoll game
|
||||
|
||||
`~/Documents/alright` (the fall-down-the-stairs game) is the portfolio's strongest *fit* for
|
||||
Box3D — an active ragdoll of 14 rigid bodies and 12 joints with PD motor control, already paying
|
||||
`physics_ticks_per_second=120` and `solver_iterations=16` and hand-picking `continuous_cd` on
|
||||
head/torso/pelvis, which is the signature of fighting a solver.
|
||||
|
||||
**It is blocked:** that ragdoll is built from `Generic6DOFJoint3D`, which the binding does not
|
||||
implement. Watch that single line item upstream — when Generic6DOF lands, `alright` becomes the
|
||||
real experiment, and the question to answer is whether the 120 Hz / 16-iteration tax can come
|
||||
back down.
|
||||
|
||||
Elsewhere in the portfolio physics is 2D, absent, or deliberately bespoke (Shitbox's car is a
|
||||
raycast-suspension `RigidBody3D` with a hand-written tyre model — its feel lives in *its* code,
|
||||
not the solver; Destroyulator's cascade is a support graph). `toastsim` is the only browser game
|
||||
with a real 3D solver need and it already runs Rapier.
|
||||
|
||||
## 5. Provenance and maturity — worth being clear-eyed about
|
||||
|
||||
**Box3D** — Erin Catto (author of Box2D), MIT, portable C17, data-oriented, ARM NEON SIMD,
|
||||
cross-platform determinism, record/replay. Repo created 2026-05-10, announced 2026-06-30,
|
||||
~5,900 stars, actively developed. Shipping adoption: Facepunch's **s&box** and the **Esoterica**
|
||||
engine. This is a serious engine by someone with 20+ years of solver experience.
|
||||
|
||||
**godot-box3d** (bearlikelion) — created 2026-07-02, ~134 stars, **zero releases**, and its README
|
||||
says plainly: *"Status: early and experimental."* Performance benchmarking is still on its own
|
||||
to-do list, so no one has yet shown it beats Jolt at anything. Modelled on godot-jolt's approach.
|
||||
There are **no prebuilt binaries** — build from source (~30 s on this machine).
|
||||
|
||||
Practical consequence: this project is a **testbed**, not a bet. Jolt remains the sane default for
|
||||
anything shipping; the value here is being able to answer engine questions with measurements
|
||||
instead of opinions.
|
||||
|
||||
## 6. Browser note
|
||||
|
||||
Box3D builds to **WASM** — its CMake maps the SSE2 path onto wasm SIMD128 and supports pthreads.
|
||||
But it ships **no JS bindings**, so a browser game would mean hand-writing that layer. Rapier
|
||||
already gives you a polished JS API. Relevant if a web pinball or web physics toy is ever floated.
|
||||
|
||||
---
|
||||
|
||||
*Cross-reference: this project's own measured results — Box3D never tunnelling even with CCD off,
|
||||
and the inverted hinge-motor sign versus Jolt/GodotPhysics — are in [README.md](README.md). That
|
||||
sign inversion is a genuine drop-in-compatibility bug worth reporting upstream.*
|
||||
@ -78,6 +78,12 @@ Two real findings, half a second of headless runtime each:
|
||||
2. With the motor overdriving into them, **Box3D enforces the angular limits hard**
|
||||
while Jolt lets this setup punch far through them.
|
||||
|
||||
## Engine notes
|
||||
|
||||
Upstream Box3D research — Apple Silicon threading (**cap workers at 24, not 32**), the benchmark
|
||||
harness and how to re-run it, what godot-box3d does *not* support, and why this game became the
|
||||
testbed — is in [BOX3D_ENGINE_NOTES.md](BOX3D_ENGINE_NOTES.md).
|
||||
|
||||
## Rebuilding the extension
|
||||
|
||||
```bash
|
||||
|
||||
Loading…
Reference in New Issue
Block a user