Fix Physics3D running out of memory when stacking scenes with "Pause and start a new scene" by Bouh · Pull Request #9250 · 4ian/GDevelop · GitHub
Skip to content

Fix Physics3D running out of memory when stacking scenes with "Pause and start a new scene" - #9250

Open
Bouh wants to merge 1 commit into
masterfrom
fix-physics3d-push-scene-oom
Open

Bouh wants to merge 1 commit into
masterfrom
fix-physics3d-push-scene-oom

Conversation

@Bouh

@Bouh Bouh commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #8640

Problem

The Jolt WASM module used by Physics3D has a fixed 128 MB heap (no memory growth: emscripten_resize_heap aborts with OOM). Each JoltInterface created with the default settings takes about 20 MB, so only 6 physics worlds fit.

A scene paused with "Pause and start a new scene" kept its Jolt world alive, as it was only freed when the scene was unloaded. Stacking scenes with Physics3D objects crashed the game with RuntimeError: Aborted(OOM) in new JoltInterface at the 6th stacked scene.

Fix

In Physics3DSharedData:

  • When a scene is paused, releaseJoltWorld() stores the linear and angular velocities of every body, destroys the bodies and frees the Jolt world.
  • When the scene is resumed, restoreJoltWorld() recreates the Jolt world and the bodies with their velocities.
  • PhysicsCharacter3D and PhysicsCar3D rebuild their own Jolt objects (character, vehicle constraint) lazily, with the mechanism already used when a behavior is deactivated then activated again.

What is lost across a pause: contacts in progress (collision "started" conditions trigger again after the resume), the sleeping state of bodies, and the internal state of characters (like being on the floor).

Performance before/after

Measured on the reproduction project of the issue (scene "Bug": 200 dynamic boxes, the scene pushes a new copy of itself every 0.5 s).

Measure master This PR
Stacked scenes before crash 5 (Aborted(OOM) at the 6th) 59+, no crash (the test stopped at 60)
Average frame time, first 150 frames (gameplay test) 5.98 ms 5.98 ms
Worst frame, first 150 frames (gameplay test) 71.1 ms 72 ms
Pausing a scene with 200 bodies 0.02 ms 0.45 ms
Resuming a scene with 200 bodies 0.02 ms 2.45 ms
Real preview: average FPS over 10 s crashes after about 3 s 143 FPS (6.96 ms per frame)
Real preview: worst frame (when a new scene is pushed) crashes after about 3 s about 207 ms

The cost of the fix is only paid when a scene is paused or resumed: a few milliseconds for 200 bodies. Frame time is unchanged.

How to test

  1. Open the reproduction project attached
    Repro8640-Physics3DPushSceneOOM.zip

Videos

Crash on Master
https://github.com/user-attachments/assets/fbeaaaf6-a5a8-43f6-a184-2cfa1cf9d237

This PR
https://github.com/user-attachments/assets/b0251318-29cf-4f9b-930b-510ab05333bf

…and start a new scene"

The Jolt world of a paused scene is now freed and rebuilt when the scene is resumed, with the velocities of the bodies.
@Bouh
Bouh requested a review from 4ian as a code owner October 15, 2026 14:19
@github-actions

Copy link
Copy Markdown
Contributor

⏱️ Game engine benchmarks: ✅ no significant change on the 31 benchmarks

All the 31 benchmarks and how they are measured
Benchmark Base This version Change Range between rounds
≈ platformer objects jumping in loop(1000x) 129.0ms 132.8ms +2.9% -0.8% to +13.2%
≈ hitBoxesCollisionTest of 10 objects with 10 objects(10000x) 83.2ms 82.7ms -0.6% -9.4% to +3.0%
≈ hitBoxesCollisionTest of 30 objects with 30 objects(1112x) 25.3ms 24.9ms -1.4% -11.9% to +3.8%
≈ hitBoxesCollisionTest of 100 objects with 100 objects(100x) 8.261ms 8.262ms +0.0% -2.1% to +1.9%
≈ hitBoxesCollisionTest of 500 objects with 500 objects(4x) 1.867ms 1.894ms +1.4% -1.3% to +3.0%
≈ hitBoxesCollisionTest of 1000 objects with 1000 objects(1x) 1.021ms 1.035ms +1.3% -1.9% to +7.7%
≈ hitBoxesCollisionTest of 1 object with 1000 objects(1000x) 54.0ms 53.2ms -1.5% -3.8% to +0.9%
≈ hitBoxesCollisionTest of 10 objects with 1000 objects(100x) 11.2ms 11.4ms +2.2% -5.9% to +9.0%
≈ hitBoxesCollisionTest of 1000 objects with 1 object(1000x) 56.8ms 58.8ms +3.5% -7.1% to +6.6%
≈ hitBoxesCollisionTest of 500 objects with themselves(4x) 1.294ms 1.298ms +0.3% -2.3% to +3.1%
≈ setAngle and setLength(100000x) 11.4ms 11.4ms +0.0% -0.2% to +0.5%
≈ convertCoords(100000x) 21.4ms 21.6ms +0.9% -0.1% to +1.8%
≈ convertInverseCoords(100000x) 21.4ms 21.4ms -0.1% -0.5% to +0.3%
≈ collisionTest between two overlapping rectangles(120000x) 47.4ms 48.0ms +1.3% -2.7% to +3.9%
≈ collisionTest between two non overlapping rectangles(120000x) 11.1ms 11.1ms +0.2% -3.1% to +1.8%
≈ getAABB of a non rotated, default center(180000x) 19.1ms 19.0ms -0.4% -2.0% to +0.4%
≈ getAABB of a rotated, default center(180000x) 19.0ms 19.0ms -0.2% -2.0% to +0.4%
≈ getAABB of a non rotated, non default center(180000x) 20.2ms 20.1ms -0.6% -3.6% to +7.2%
≈ getAABB of a rotated, non default center(180000x) 61.2ms 60.2ms -1.7% -3.6% to +3.1%
≈ getAABB of a non rotated sprite, with custom hitboxes, origin and center(60000x) 19.7ms 19.9ms +0.6% -2.0% to +3.6%
≈ getAABB of a rotated sprite, with custom hitboxes, origin and center(60000x) 67.6ms 67.6ms +0.1% -0.3% to +0.5%
≈ get(400000x) 11.6ms 11.6ms +0.0% -0.3% to +0.5%
≈ Scene: Static sprites (10000) 4.941ms 5.268ms +6.6% -4.8% to +26.1%
≈ Scene: Moving and rotating sprites (5000) 6.460ms 6.568ms +1.7% -6.5% to +16.2%
≈ Scene: Collisions and separation (500 vs 500) 15.8ms 15.3ms -3.2% -8.1% to +3.6%
≈ Scene: Physics 2D (500 bodies) 3.399ms 3.270ms -3.8% -5.7% to +2.6%
≈ Scene: Physics 3D (500 bodies) 12.4ms 13.0ms +4.5% -5.2% to +8.9%
≈ Scene: Platformer characters (300) 1.014ms 0.997ms -1.6% -7.3% to +6.1%
≈ Scene: Text updates (1000) 54.7ms 54.0ms -1.3% -3.3% to +1.7%
≈ Scene: Objects creation and deletion (100 per frame) 1.890ms 1.919ms +1.5% -12.7% to +20.7%
≈ Scene: Heavy events (variables, loops, strings) 1.505ms 1.594ms +5.9% -2.1% to +16.5%

✅ The counters (draw calls, objects count and final state) of the 9 scene benchmarks run on both versions are unchanged.

Comparing 5f89345c to its base 02382da7 (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.

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.

Physics3D: OOM when stacking scenes (Jolt world not freed on scene pause)

1 participant