Over the past week or so I've been working on getting better collision shapes for the hitboxes for the 'mon in my game. In doing this, I found that the frame rate seemed to noticeably decrease compared to just using a large capsule for the hitbox. So I decided to do some rudimentary benchmarking of the different collision shapes Bullet offers.

For my hitboxes, I added support for the btMultiSphereShape, a convex hull of a set of spheres, to U3D[^1] and my (forked) Blender Exporter. This shape is pretty nice a few spheres are able to nicely model segments of the body (roughly corresponding to the animation skeleton: upper arm, fore arm, head, neck, chest, waist, etc.) for collision purposes. It's not quite as exact as a triangle mesh, but it's actually solid, not hollow, so fewer missed collisions should happen, and it's pretty quick to make with Blender's geometry nodes. With support added, and examples for my two characters for the mini battler demo/game I'm working on, the initial tests seemed to give a drop in frame rate from about 100 to 70, give or take. So I felt I needed to try benchmarking what was going on (as the profiler did indicate a lot of time spent in some seemingly related bullet physics functions).

Benchmark

For the benchmarking, I wanted it to be easy for me to write and fairly representative of an actual game scenario. So I simply adapted the Physics Stress Test Sample to use different shapes, which can be set through command line arguments. Further, the number of rigid bodies that are created can be set through an argument so I can ascertain the maximum number of shapes I should aim to use at one time. Lastly, I wanted to get an idea of how many spheres it's okay to use per multi-sphere hull, and I also wanted to compare to the alternative of using a btCompoundShape with multiple sub-shapes[^2] instead of the multi-sphere hull. So the number of sub-shapes (for non-multi-sphere-hull shapes) or spheres (for multi-sphere hull) is also set by an argument.

This leads to the following results when I run the benchmark on my (fairly weak, old i5) desktop. Take them with a grain of salt, as there are a few issues with the benchmark:

  1. There are only a few runs. I was aiming for 6, but
  2. My desktop had an issue where the X server crashed 3/4 through the benchmark runs, so there were actually only 4 to 5 runs per shape.
  3. The results varied a lot. I throw out the outliers beyond 2 standard deviations outside of the median (and then recalculate the standard deviation for use as the error bars).
  4. I limit each run to only 15 seconds or after the last-created rigid body is deactivated, whichever comes first. This could possibly cut off the run before it reaches the minimum FPS (after the rigid bodies stop moving, they are deactivated and FPS increases again as there is less work being done by Bullet)
  5. Anything slower than 60FPS should be regarded as suspect, as that is the FPS for the fixed time step update that bullet is using. So anything slower than that is subject to the "Spiral of Death" where multiple physics steps will be calculated in one frame, which slows the render frame rate, which requires more physics steps to be calculated in one frame, and so on (until the physics simulation settles and bodies are deactivated).
  6. I only use solid shapes, so no triangle meshes. My guess is they are on the slower side, but I haven't tested them because I am only to looking use shapes that know if another shape is inside them.

Shapes-Benchmark

Even with the limitations, there are still some conclusions we can draw from the benchmark.

  1. The baseline time per frame is about 5.5 milliseconds.
  2. Individual (non-compound) shapes are much faster than compound shapes (excluding the multi-sphere hull)
  3. Spheres and Boxes are the fastest shapes (note the 2 sub-shape results).
  4. The multi-sphere hull is about the same speed as the 2 (or maybe 3) sub-shape compound shapes, up to somewhere around 6-8 spheres for the hull.
  5. The spiral of death seems to be real, but varied note the sharp increases slower than 60FPS, but how the results come back down with even more shapes due to the variability between runs.
  6. While the multi-sphere hull is rather slow, a single normal convex hull (based on vertices) is fast. Though another rudimentary test suggested that just using around 32 points to model the multiple spheres gives worse performance than the multi sphere hull.

Conclusions for my game

For my 60FPS target, with an additional 5ms budget for animations, visual effects, AI, etc., I can afford up to around 100 multisphere hull shapes (i.e. hitboxes) divided between all the 'mon in a battle. This should be sufficient for up to 6 'mon in a battle, assuming no more than 1516 multisphere hull shapes for each 'mon. This is pretty reasonable: 2 per limb, 2 for the body, 1 for the neck, 1 for the head, maybe a few more for the tail (or maybe not the tail may be too much of a handicap). Generally no more than 4 limbs, which gives 8+2+1+1+3 = 15, right where the benchmark suggests I should aim.


[^1]: U3D is the fork of Urho3D I am now using after the original project fell apart.

[^2]: wrapping in a btCompoundShape is handled for us internally by U3D if we just add multiple CollisionShapes to a Node.

Previous Post Next Post