Announcing the Rapier physics engine

In our announcement last week, we briefly mentioned this new physics engine we have been working on during the past 5 months. Today we are officially releasing it for the first time: the project Rapier; a set of two 100% rust libraries rapier2d and rapier3d for 2D and 3D physics simulations for games, animation, and robotics.

rapier logo

This post will be quite long, so here are all the different sections:

Presenting Rapier

Rapier is the successor of nphysics and focuses on performance first. Just like nphysics it is split into two crates: rapier2d and rapier3d for 2D and 3D physics respectively. It is designed to be fast and multithreaded right from the beginning. It is also designed to require less incremental compilation times because the data structures it defines are not generic.

In release mode, Rapier runs 5 to 8 times faster than nphysics , making it close to the performance of (the CPU version of) NVidia PhysX and slightly faster than Box2D as you will see in the benchmark sections. Rapier is only at its beginning, so many features are still missing. However some performance optimizations like parallelism, and SIMD have been integrated right from the start.

There already are a few key features that makes Rapier stand out. Since they may affect compilation times and/or performance, they are disabled by default and need to be enabled explicitly through cargo features:

  1. Serialization: if the serde-serialize feature of Rapier is enabled, every physics component will be serializable using serde. This means that you can take a deep snapshot of the physics state and restore it later. This snapshot can even be saved on disk or sent through network.
  2. Cross-platform determinism: if the enhanced-determinism feature of Rapier is enabled, it will behave in a bit-level cross-platform deterministic way in all platforms that comply with the IEEE 754-2008 floating point standard. This means that if you run the same simulation with the same initial states on two different machines (or browsers) you will get the exact same results. Here "bit-level" determinism means that if you serialize the physics state after the same number of timesteps on two different machines, you will obtain the exact same byte array for both: you may compute a checksum of both snapshots and they will be identical. All this doesn't apply to platforms with pointer size smaller than 32-bit, and on platforms that don't comply to IEEE 754-2008 strictly.

See that section for a comparison between Rapier and nphysics features.

Reaching out to other communities: Bevy and JavaScript

Bevy WASM JS

Writing a physics engine is hard. There are not a lot of choices out there and most of them are written in C++. With nphysics we wanted to provide an open-source 100% rust physics solution for the Rust community. With Rapier we want to go one step further by contributing to as many communities in need of a physics engine as we can. This is why we are starting, right at the beginning of the Rapier story, by providing:

  • Official JavaScript bindings for the WASM version of Rapier. These binding are generated using wasm_bindgen and are published to NPM as the packages @dimforge/rapier3d and @dimforge/rapier2d. While multiple physics solutions already exist for JavaScript they are either slow (because they are manually written in JS like cannon.js or oimo.js), or not officially maintained by their original developers (because they are ported from C++ using Emscripten, like box2d.wasm, ammo.wasm, or physx.wasm). By providing official wasm builds and JS bindings, we are making sure to provide the best support, documentation, and continuous updates to the JS community.
  • Official plugins for the Bevy game engine. They are available as the bevy_rapier2d and bevy_rapier3d crates. The Bevy game engine has recently been released as an efficient, fast-to-compile, and easy to use, data-oriented game engine. It is still at its early state and is lacking any physics feature. We believe physics support is a very high-value feature to have in a game engine. By providing official plugins we want to make sure the Bevy community can benefit from the Rapier physics engines quickly and easily.

There are so many more communities we would like to contribute to but don't have manpower to support all of them just now. Other integrations and languages will come in the future.

We also plan to create official plugins for the Amethyst game engine but have not started yet. We are waiting for the migration of Amethyst to the legion ECS solution before starting to work on this.

note

Examples using the 2D JS bindings are available on GitHub: 2D and 3D. You can see these demos running there for 2D and there for 3D. In these demos, the physics simulation runs inside of a web worker and the rendering is performed by PixiJS and Three.js.

Feature comparison with nphysics

Rapier does not have as many features as nphysics yet, but it also has a few features nphysics does not have. Here are comparative tables of both physics engines:

Dynamics featuresRapiernphysics
Rigid-body physics
Kinematic bodies
Rigid-body islands and sleeping
Joint constraints
Joint constraint limits and motors
Reduced-coordinate joints
Reduced-coordinates joint limits and motors
Conveyor belts
Deformable bodies
Fluids (integration with Salva)
Geometry featuresRapiernphysics
Colliders
Sensors
Contact/proximity events
Contact graph
Continuous Collision Detection
Ray-casting
Convex-casting
Performance and portability featuresRapiernphysics
Floating-point cross-platform determinism
Parallelization
SIMD
Serialization
JavaScript bindings
Integration to Bevy
Integration to Amethyst
Fixed-point cross-platform determinism
64-bits physics

Today nphysics is more mature than Rapier. Our first goal during the next few months is to bring Rapier at the same level as nphysics featurewise, while keeping the significant performance improvements and better accuracy.

Benchmarks

Alright, we claimed that Rapier is nearly as fast as the CPU version of PhysX, 5 to 10 times faster than nphysics, and slightly faster than Box2D. It is now time to prove these claims using benchmarks.

info

Keep in mind that Rapier is still at a early development stage. It does not have as many features as the ofther physics engines involved in this benchmark. However, our objective is to ensure that the future addition of new features to Rapier don't reduce the level of perfomance you see here.

In the subsequent benchmarks we ran a set of stress tests using four different physics engines:

  1. Rapier using our rapier3d crate for 3D and rapier2d for 2D.
  2. PhysX 4 using the physx crate.
  3. Box2d using the wrapped2d crate.
  4. nphysics using our nphysics3d crate for 3D and nphysics2d for 2D

Independently of the chosen physics engine, all scenes are always initialized in the exact same way (same bodies, same colliders at the same initial positions) and with the following parameters:

  • Rust compiler and flags: rustc 1.46.0-nightly, --release, --features simd-nightly, codegen-units = 1.
  • Timestep length: 0.016 (i.e. 16 milliseconds).
  • Number of velocity iterations: 4 for Rapier, nphysics, and Box2D. 1 for PhysX.
  • Number of position iterations: 1 from Rapier, nphysics, and Box2D. 4 for PhysX.
  • Targets: native CPU (WebAssembly versions and PhysX on GPU have not been benchmarked.)
  • Solvers: variations of PGS. (The PhysX 4.0 TGS solver has not been benchmarked. We used the default ePGS solver.)
  • Number of threads: 1.
note

In this benchmark we don't use multithreading. A benchmark with multithreading enabled, involving only multithread physics engine will be the subject of another blog post.

Each 3D benchmark is run on two different machines because we observed performance differences between PhysX and Rapier depending on the processor:

  1. A desktop computer, running Ubuntu, equipped with an AMD Ryzen 9 3900X CPU, 3.8GHz.
  2. A MacBook Pro (plugged to a power outlet), running Mac OS, equipped with an Intel Core i7 7920HQ, 3.1GHz.

The 2D benchmarks are run only on the AMD Ryzen 9 3900X CPU (the relative performance remain the same with the Inter Core i7 CPU).

3D Benchmark: Rapier vs. PhysX vs. nphysics

A few notes are in order regarding PhysX in this benchmark. First, we don't use the same number of iterations for Rapier and PhysX. For Rapier and nphysics we use 4 velocity iterations and 1 position iteration. It is the other way round for PhysX: 1 velocity iteration and 4 position iterations. This is the most sensible configuration because:

  • PhysX developers advise to increase the number of position iterations for better stability, and leave to 1 the number of velocity iterations.
  • PhysX itself (independently from this benchmark) uses 1 velocity iteration and 4 position iterations by default. It yields more stable simulations than using 4 velocity and 1 position without performance difference.
  • Rapier and nphysics use a solver different from PhysX's. With our solver, it is recommended to increase the number of velocity iterations instead of position iterations, and leave the number of position iterations to 1.

The PhysX benchmarks will include two performance curves:

  1. One performance curve using their default ePATH friction model. This is a simplified friction model, faster to compute, but less realistic. Rapier and nphysics don't implement a similar model yet.
  2. One performance curve using their eTWO_DIRECTIONAL friction model. This is similar to the friction model used by Rapier and nphysics.

8.000 stacked balls

In this benchmark there are 8000 balls falling on a ground also composed of balls. In the end, they form 400 independent small stacks of balls. bench balls bench balls bench balls

3.000 falling boxes

In this benchmark, there are about 3000 small cubes falling on a large cube floor in a completely unordered way. bench boxes bench boxes bench boxes

Pyramid of 4.900 boxes

In this benchmark, there is a set of 4900 boxes falling in such a way that they form a single large pyramid in the end. bench pyramid bench pyramid bench pyramid

Triangle mesh with 1.500 falling boxes and balls

In this benchmark, the floor is modelled as a triangle mesh composed of 800 triangles. There are 1500 small boxes and 1500 small balls falling on this mesh. bench trimesh bench trimesh bench trimesh

KEVA planks tower with 5.320 planks

In this benchmark, 5230 rectangular rigid bodies are used to form a tower, much like what can be achieved with real-world KEVA wood planks. During the simulation, the tower breaks down to end up with a large pile. Note that the tower breaks down because only 4 velocity iterations are used for this simulation. With about 15 velocity iterations, the tower would remain stable when using Rapier. bench balls bench keva tower bench keva tower

19.800 interconnected ball joints

In this benchmark, we have little less than 20.000 ball joints (aka. spherical joints). These joint connect rigid bodies in such a way that we end up with a single large fabric-like sheet. bench balls bench ball joints bench ball joints

20.000 fixed joints

In this benchmark, we have 20.000 fixed joints keeping all balls from moving wrt. each other. bench fixed joints bench fixed joints bench fixed joints

8.000 revolute joints

In this benchmark, we have 8.000 revolute joints attached in a way that forms several independent chains. bench revolute joints bench revolute joints bench revolute joints

3D Benchmark: Conclusion

From these benchmark we can see that:

  • PhysX and Rapier are both 4 to 8 times faster than nphysics. The gap would be greater (5 to 10 times faster) with multithreading enabled.
  • Rapier and PhysX have close performance. When targeting the Ryzen 9 CPU, Rapier ends up being slightly faster in several cases. However, when targeting the Intel CPU PhysX ends up being slightly faster than Rapier.
  • In terms of simulation quality, PhysX and Rapier are quite similar. Joints from PhysX look slightly stiffer because convergence for fixed joints are more quickly achieved. Joints from Rapier and PhysX are both much more stable than joint constraints from nphysics.

2D Benchmark: Rapier vs. Box2D vs. nphysics

12.500 stacked balls

In this benchmark there are 12.500 balls falling on a ground composed of balls. bench balls bench balls The Box2D times are very noisy here. We suspect this is caused by their broad-phase implementation.

3.380 falling boxes

In this benchmark, there are about 3.380 small cubes falling on a large cubic "cup" in a completely unordered way. bench boxes bench boxes

Pyramid of 5.050 boxes

In this benchmark, there is a set of 5.050 boxes falling in such a way that they form a single large pyramid in the end. bench pyramid bench pyramid

19.800 interconnected ball joints

In this benchmark, we have little less than 20.000 ball joints (which are the same as revolute joints in 2D). These joint connect rigid bodies in such a way that we end up with a single large fabric-like sheet. The performance of Box2D go really crazy on this benchmark because its simulation breaks down (lack of numerical stability). bench balls bench ball joints

28.840 fixed joints

In this benchmark, we have 28.840 fixed joints keeping all balls from moving wrt. each other. bench fixed joints bench fixed joints

12.500 prismatic joints

In this benchmark, we have 12.500 prismatic joints attached in a way that forms several independent chains. Lower joint limits have been enabled. bench revolute joints bench revolute joints

2D Benchmark: Conclusion

From these benchmarks we can see that.

  • Rapier is generally slightly faster than Box2d in these benchmarks, especially when joints are involved.
  • Joint constraints from Rapier are much more stable than Box2d. In particular, prismatic joints from Box2d are subject to lots of jitter, and the ball joints scenario completely exploded.

Running the benchmarks yourself

If you would like to see how these benchmarks perform on your own computer, you may run them manually:

  • For 2D benchmarks, execute the command cargo run --release --features simd-nightly --features other-backends -- --bench on the examples2d directory of the rapier repository.
  • For 3D benchmarks, execute the command cargo run --release --features simd-nightly --features other-backends -- --bench on the examples3d directory of the rapier repository.

If you are using a stable version of the Rust compiler, you will have to replace --features simd-nightly by --features simd-stable so it compiles properly.

The benchmark will run and the outputs will be writen as CSV files created on the current directory. There will be one CSV file per simulated scenario. A CSV file will contain multiple columns, one per running physics engine.

Conclusion

The development of Rapier started only five months ago and already shows very promising results. Its performance make it a great 100% Rust alternative to PhysX as long as you don't need a GPU-based solution. For 2D it is often slightly better than Box2D performance-wise, but it still lacks some features to be able to completely replace it. Compared to nphysics, Rapier is at a whole different level both in terms of performance and joint constraint stability. Finally, thanks to its JavaScript bindings, Rapier is both the most efficient and numerically stable Web physics engine available on NPM.

Optional features like snapshotting and bit-level cross-platform determinism make Rapier appealing for multiplayer and collaborative uses.

During the next ~8 months, our goal will be to implement in Rapier all the features that currently exist on nphysics, including CCD, reduced-coordinates joints, and soft-bodies. If you need some specific features (even that did not exist on nphysics), please feel free to contact us.

If you would like to support us with the development of Rapier, please consider sponsoring us on GitHub sponsor.

Thank you for your support!