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:
m3ultra 2026-08-06 08:54:25 +10:00
parent 2ca67bb28b
commit 804e4c0fb8
2 changed files with 136 additions and 0 deletions

130
godot/BOX3D_ENGINE_NOTES.md Normal file
View 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.*

View File

@ -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