Use the multithreaded build of Jolt for the 3D physics when SharedArrayBuffer is available by Bouh · Pull Request #9249 · 4ian/GDevelop · GitHub
Skip to content

Use the multithreaded build of Jolt for the 3D physics when SharedArrayBuffer is available - #9249

Open
Bouh wants to merge 1 commit into
physics3d-cache-mesh-shapesfrom
physics3d-multithread
Open

Bouh wants to merge 1 commit into
physics3d-cache-mesh-shapesfrom
physics3d-multithread

Conversation

@Bouh

@Bouh Bouh commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Repro scene: 453 static Building A models (mesh shape), 1445 dynamic Ship Large models falling and piling up.

Single-threaded Multithreaded (4 worker threads)
Physics3D time per frame (gameplay test, fixed 1/60 s step) ~18-20 ms ~8.8 ms
FPS in the preview after 10 s ~20 ~35
FPS in the preview after 15 s ~20 ~29
Jolt step alone (Chrome, 450 static + 1600 dynamic boxes, steps 60-300) 7.30 ms 0.74 ms

Videos

physics3d-monothread-15s
https://github.com/user-attachments/assets/fa5c8308-3596-4c46-b935-6ec796ddeeb8

physics3d-multithread-15s
https://github.com/user-attachments/assets/6c2da90b-d16d-4527-8457-48f818c964a4

…ayBuffer is available

- Load jolt-physics.multithread (same version, 0.39.0) when SharedArrayBuffer is
  available, with up to 4 worker threads, and fall back to the single-threaded build.
- The contact listener is called by the worker threads during the step, where the
  game objects don't exist: it now only writes the contacts into a buffer of the
  WebAssembly memory (also given to the workers with configureWorkerScripts),
  read after the step to call onContactBegin/onContactEnd.
- Enable SharedArrayBuffer in Electron, as previews are not cross-origin isolated.
- Optimize the broad phase before a step when many bodies were added.
@Bouh
Bouh requested a review from 4ian as a code owner October 15, 2026 11:30
@github-actions

Copy link
Copy Markdown
Contributor

⏱️ Game engine benchmarks: ⚠️ 9 removed or failing

Benchmark Base This version Change Range between rounds
⚠️ Scene: Static sprites (10000) 4.543ms - - -
⚠️ Scene: Moving and rotating sprites (5000) 6.219ms - - -
⚠️ Scene: Collisions and separation (500 vs 500) 15.4ms - - -
⚠️ Scene: Physics 2D (500 bodies) 3.327ms - - -
⚠️ Scene: Physics 3D (500 bodies) 11.8ms - - -
⚠️ Scene: Platformer characters (300) 0.994ms - - -
⚠️ Scene: Text updates (1000) 53.5ms - - -
⚠️ Scene: Objects creation and deletion (100 per frame) 1.777ms - - -
⚠️ Scene: Heavy events (variables, loops, strings) 1.484ms - - -
All the 31 benchmarks and how they are measured
Benchmark Base This version Change Range between rounds
≈ platformer objects jumping in loop(1000x) 127.9ms 118.9ms -7.0% -11.5% to +8.9%
≈ hitBoxesCollisionTest of 10 objects with 10 objects(10000x) 82.8ms 82.3ms -0.6% -2.5% to +7.5%
≈ hitBoxesCollisionTest of 30 objects with 30 objects(1112x) 25.7ms 26.4ms +2.9% -16.6% to +11.5%
≈ hitBoxesCollisionTest of 100 objects with 100 objects(100x) 8.305ms 9.701ms +16.8% -21.8% to +31.9%
≈ hitBoxesCollisionTest of 500 objects with 500 objects(4x) 1.874ms 1.970ms +5.1% -0.1% to +45.6%
≈ hitBoxesCollisionTest of 1000 objects with 1000 objects(1x) 1.029ms 1.095ms +6.4% -1.9% to +34.4%
≈ hitBoxesCollisionTest of 1 object with 1000 objects(1000x) 52.8ms 52.6ms -0.4% -5.2% to +6.0%
≈ hitBoxesCollisionTest of 10 objects with 1000 objects(100x) 11.4ms 10.9ms -4.6% -6.1% to -2.4%
≈ hitBoxesCollisionTest of 1000 objects with 1 object(1000x) 57.2ms 55.5ms -3.1% -11.1% to -1.2%
≈ hitBoxesCollisionTest of 500 objects with themselves(4x) 1.309ms 1.310ms +0.1% -1.5% to +3.5%
≈ setAngle and setLength(100000x) 11.4ms 11.4ms -0.1% -0.3% to +0.4%
≈ convertCoords(100000x) 21.4ms 21.5ms +0.4% -0.5% to +1.4%
≈ convertInverseCoords(100000x) 21.4ms 21.4ms +0.1% -1.7% to +1.3%
≈ collisionTest between two overlapping rectangles(120000x) 47.7ms 47.3ms -0.9% -3.0% to +14.2%
≈ collisionTest between two non overlapping rectangles(120000x) 11.2ms 11.2ms +0.1% -3.1% to +7.2%
≈ getAABB of a non rotated, default center(180000x) 19.0ms 19.0ms -0.1% -1.7% to +5.4%
≈ getAABB of a rotated, default center(180000x) 19.0ms 19.2ms +0.7% -1.7% to +5.3%
≈ getAABB of a non rotated, non default center(180000x) 20.2ms 20.0ms -0.9% -2.3% to +1.0%
≈ getAABB of a rotated, non default center(180000x) 59.7ms 61.9ms +3.7% -2.3% to +6.6%
≈ getAABB of a non rotated sprite, with custom hitboxes, origin and center(60000x) 19.8ms 19.6ms -0.8% -7.5% to +1.0%
≈ getAABB of a rotated sprite, with custom hitboxes, origin and center(60000x) 67.4ms 67.7ms +0.4% -1.1% to +1.6%
≈ get(400000x) 11.6ms 11.6ms -0.0% -0.1% to +0.1%
⚠️ Scene: Static sprites (10000) 4.543ms - - -
⚠️ Scene: Moving and rotating sprites (5000) 6.219ms - - -
⚠️ Scene: Collisions and separation (500 vs 500) 15.4ms - - -
⚠️ Scene: Physics 2D (500 bodies) 3.327ms - - -
⚠️ Scene: Physics 3D (500 bodies) 11.8ms - - -
⚠️ Scene: Platformer characters (300) 0.994ms - - -
⚠️ Scene: Text updates (1000) 53.5ms - - -
⚠️ Scene: Objects creation and deletion (100 per frame) 1.777ms - - -
⚠️ Scene: Heavy events (variables, loops, strings) 1.484ms - - -

ℹ️ No scene benchmark ran on both versions: their counters (draw calls, objects count and final state) could not be compared.

Comparing 9b30ba5b to its base 608b3de0 (on a pull request, the version tested is the pull request merged into its base), run alternately 4 times on the same machine (AMD EPYC 7763 64-Core Processor, 4 cores, rendering without GPU). Times are the median duration of a batch of iterations (micro benchmarks) or of a frame (scene benchmarks). A change is reported only when larger than 5% and than the differences between the rounds of a same version, and measured by every pair of rounds.

@4ian

4ian commented Oct 15, 2026

Copy link
Copy Markdown
Owner

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants