Processing 19,200 Rigidbodies with wasm threading


This video was captured on an i9-13900K

try it your self: bullet-wasm (bullet-rust-wasm.netlify.app)
The number of rigidbodies will vary depending on CPU performance.

Introduction

Physics simulation is one of the biggest performance bottlenecks in web 3D applications.

Generally, physics engines running in modern browsers are all compiled to WebAssembly, and the WebAssembly modules are used from the JavaScript side.

I’ve analyzed this approach and considered which areas could be improved to eliminate performance bottlenecks:

1. Frequent calls to wasm functions from JavaScript

The number of wasm function calls can be drastically reduced by directly accessing specific parts of the WebAssembly instance memory from JavaScript,

ammo.js needs to read the transform values of btDefaultMotionState after each simulation step. If there are 100 rigidbodies in the simulation, this means that the wasm function must be called at least 100 times from JavaScript.

By accessing WebAssembly.Memory and reading it with a TypedArray, it would be possible to run the simulation with ideally only one wasm function call per frame.

2. Threading in wasm could enable larger simulations

The threading approach here refers to simultaneously simulating multiple independent physics worlds.

Some games/applications don’t require perfect collision handling for all objects.

In such cases, parallel processing of multiple physics worlds is very useful.

Using WebAssembly, we can write traditional thread-based programs more easily than using JavaScript workers and SharedArrayBuffer directly.

The demo above shows 24 physics worlds being processed in parallel using rayon-rs’s thread pool.

So, what does this project do?

This project compiles Bullet Physics to WebAssembly.

However, unlike ammo.js, it statically links Bullet Physics into a Rust project using wasm-bindgen.

By using Rust, we can easily handle multithreading with rayon-rs.

I simplified the Bullet Physics API as much as possible in Rust and exported it, allowing the physics engine to be used in JavaScript with minimal wasm function calls.


Unfortunately, I don’t have the time to develop this demo into library, and I’m not sure how many people actually want something like this. However, I hope that sharing this idea with the Babylon.js community will be meaningful.

Here is all the source code:

9 Likes

This is amazing. And I have so many questions.
I guess the main one is about the (24) physics worlds that achieve multithreading - Does that mean that different instances are available in N different world? Or does one instance only available in one world? If so, how do you achieve collisions between the “worlds”?

1 Like

Basically, this means that each instance operates within its own world. Collisions between worlds are not handled.

For kinematic or static objects, it’s possible to share them by copying the same state to all worlds, but for dynamic objects, unfortunately, it’s not possible to handle collisions across worlds while applying multithreading with this method. :frowning:

1 Like

I wonder if this is something we are already doing with Havok? That sounds like a powerful optimization! Very impressive work :ok_hand:

1 Like

Yes havok is already doing this

1 Like

Quite impressive! So sad we cannot have the same with pure JS

1 Like

Js can have threading like wasm using SharedArrayBuffer and Atomics, but devs have to code like wasm since no structured object in it (see the proposal-structs).
Also note that SharedArrayBuffer and wasm threading would require secure context(mostly means https), Cross-Origin-Opener-Policy, and Cross-Origin-Embedder-Policy, which not only requires changes on server side, but also means that you can not use iframes with that, for example, you can not embed a youtube video in your page after that.

2 Likes

@noname0310 this is so cool!

Do you know how havok would perform with this same scenario compared to your Bullet Rust rayon-rs solution?

1 Like

Yeah that’s one of the reasons I use wasm

This is pretty critical

By putting compile branches in your rust source, you can easily create separate builds with and without shared array buffers.

If the user needs an iframe or doesn’t want to write cumbersome headers, you can have them use a version of the wasm build that doesn’t use the shared array buffer.

1 Like

havok does not provide the ability to compute multiple independent physics worlds in parallel.

It could probably be implemented using only a js worker, but I have no idea how hard it would be

1 Like

Nice setup, i bet that took a while to iron out, thank you for sharing.

Any thoughts on the feasibility of using ammos opencl backend and transforming to wgsl?

1 Like

Oh I just realized that bullet physics supports gpgpu.

I just implemented the c++ stdlib ( just like polyfill in js) to compile bullet physics.

To use the openCL version of bullet physics in a browser environment would require a much more complicated process than that, and I doubt it’s even technically possible.

Thanks for the idea.

1 Like

This should be fairly complex:

  1. Use some libs like clspv, that compiles opencl to spirv
  2. Use tint from dawn or naga to compile spirv to wgsl
  3. Find bindings of webgpu like wgpu-native
  4. Impl bullet physics on top of all the stuff above
2 Likes