I'm an ex-long time "AAA" game programmer, now solo indie developer making GearBlocks, a game all about creative building, interactive machines, and gears - lots of gears!

Report RSS Unity upgrade brings performance gains

Posted by on

Now that I've removed the old RakNet networking stuff, I've been unblocked from upgrading to later versions of Unity, and so that's what I've been working on over the past couple of weeks.

Unity 2018.4

First I upgraded from 2017.4 to Unity 2018.4, this brought some significant improvements:-

  • PhysX upgraded to 3.4, I found to this be a big performance improvement, especially for constructions with a large number of rigidbodies / constraints.
  • Non allocating collision contacts. By enabling "Reuse Collision Callbacks", and using the new Collision.GetContacts() API in OnCollisionEnter / OnCollisionStay, this eliminates GC allocs and improves physics performance too.
  • Graphics jobs no longer marked as “experimental”, so I enabled this, it should take some load off the main render thread.
  • Terrain instanced rendering, fewer draw calls for rendering the terrain mesh.
  • .NET 4.x no longer experimental, so I switched over to this from version 3.5.
  • IL2CPP scripting back-end for standalone builds, compiles to native binary, should improve performance and is more secure than shipping IL assemblies.

Unity 2019.3

Then moving on to 2019.3, which included a PhysX upgrade to version 4.1, this didn't seem to provide any additional performance gains out of the box, but it does introduce some intriguing possibilities:-

  • New temporal Gauss Seidel solver (see below).
  • Automatic box broad-phase pruning, I tried this and didn't notice any performance improvement, but I need to do some more tests to be sure.

Temporal solver

This new solver is supposed to improve stability for jointed rigidbodies with large mass ratios. I was hoping that it would eliminate or at least reduce the need for "fake" masses. For example, the gears right now all have the same (quite large) mass regardless of their size, to prevent them slipping, and it would be nice to give them real masses instead.

Unfortunately in my experiments this didn't work out, in fact even leaving the gears with their fake masses, they wouldn't run smoothly at all. It was almost as if the constraints between them were now too stiff, so instead of locking them together I tried using a limit with a small distance, to introduce some "backlash" to relax things slightly. This definitely helped but was still not completely smooth.

Sadly though, there were many other problems too, such as CV joints glitching out like crazy under even small torque loads, and wheels sinking through the ground. Not to mention spring dampers being way too strong (I guess everything would need re-tuning).

So I decided to leave this for now and stick with the old projected solver. There is a potential massive performance gain to be had with the temporal solver however, due to its faster convergence. I found that to achieve the same approximate constraint "stiffness", it seems to only need the number of solver steps to be scaled linearly with the sim fixed time step, rather than with the square of the time step like the old projected solver.

I think this new solver will be worth revisiting again if I have time at some point in the future.

Performance results

A talented GearBlocks builder sent me this awesome snow groomer creation! It's quite physics heavy due to the tank tracks (a large number of rigidbodies, constraints, and collision contacts), so I've been using it as a test case to do some before and after performance comparisons.

I also compared with "Continuous Contact Detection" under the gameplay settings both on and off. Turning this off disables OnCollisionStay, which makes a big performance difference when there are a large number of collision contacts (the only downside being you get no sliding sound effects).

Here are some results (running on my dev machine at 1920x1080 full-screen) with the snow groomer:-

  • 2017.4 / PhysX 3.3, OnCollisionStay enabled: <10 fps.
  • 2017.4 / PhysX 3.3, OnCollisionStay disabled: <10 to 23 fps.
  • 2019.3 / PhysX 4.1, OnCollisionStay enabled: 14 to 21 fps.
  • 2019.3 / PhysX 4.1, OnCollisionStay disabled: 30 to 40 fps.

Note that there's a range on these numbers because performance varies depending on how things are moving, how many collisions are happening, etc.

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: