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.
This post will be quite long, so here are all the different sections:
- Presenting Rapier
- Feature comparison with nphysics
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:
- Serialization: if the
serde-serializefeature 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.
- Cross-platform determinism: if the
enhanced-determinismfeature 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.
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 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.
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:
|Rigid-body islands and sleeping||✅||✅|
|Joint constraint limits and motors||❌||❌|
|Reduced-coordinates joint limits and motors||❌||✅|
|Fluids (integration with Salva)||❌||✅|
|Continuous Collision Detection||❌||✅|
|Performance and portability features||Rapier||nphysics|
|Floating-point cross-platform determinism||✅||❌|
|Integration to Bevy||✅||❌|
|Integration to Amethyst||❌||❌|
|Fixed-point cross-platform determinism||❌||✅|
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.
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.
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:
- Rapier using our rapier3d crate for 3D and rapier2d for 2D.
- PhysX 4 using the physx crate.
- Box2d using the wrapped2d crate.
- 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:
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
- Number of threads: 1.
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:
- A desktop computer, running Ubuntu, equipped with an AMD Ryzen 9 3900X CPU, 3.8GHz.
- 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:
- One performance curve using their default
ePATHfriction model. This is a simplified friction model, faster to compute, but less realistic. Rapier and nphysics don't implement a similar model yet.
- One performance curve using their
eTWO_DIRECTIONALfriction 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.
3.000 falling boxes
In this benchmark, there are about 3000 small cubes falling on a large cube floor in a completely unordered way.
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.
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.
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.
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.
20.000 fixed joints
In this benchmark, we have 20.000 fixed joints keeping all balls from moving wrt. each other.
8.000 revolute joints
In this benchmark, we have 8.000 revolute joints attached in a way that forms several independent chains.
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. 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.
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.
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).
28.840 fixed joints
In this benchmark, we have 28.840 fixed joints keeping all balls from moving wrt. each other.
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.
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 -- --benchon the
examples2ddirectory of the rapier repository.
- For 3D benchmarks, execute the command
cargo run --release --features simd-nightly --features other-backends -- --benchon the
examples3ddirectory 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.
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!