From 804e4c0fb8695958733d23ef4330d71ac80d09f5 Mon Sep 17 00:00:00 2001 From: m3ultra Date: Thu, 6 Aug 2026 08:54:25 +1000 Subject: [PATCH] Add Box3D upstream research notes for the testbed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- godot/BOX3D_ENGINE_NOTES.md | 130 ++++++++++++++++++++++++++++++++++++ godot/README.md | 6 ++ 2 files changed, 136 insertions(+) create mode 100644 godot/BOX3D_ENGINE_NOTES.md diff --git a/godot/BOX3D_ENGINE_NOTES.md b/godot/BOX3D_ENGINE_NOTES.md new file mode 100644 index 0000000..0112313 --- /dev/null +++ b/godot/BOX3D_ENGINE_NOTES.md @@ -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 ` one scene · `-w ` a single worker count · `-t ` max threads · +`-r ` 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.* diff --git a/godot/README.md b/godot/README.md index 9aa5ef5..6838d47 100644 --- a/godot/README.md +++ b/godot/README.md @@ -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