top of page

Nanite Performance Testing

With the introduction of Unreal 5, we have a new way of rendering 3D meshes: with Nanite. There's a lot of buzz around this topic, and many studios are testing it out in their game development pipelines right now (mine included). I've been running nanite tests for a while now and I wanted to share my findings about it.

​

So let's look at the costs of using nanite and the best situations to opt into it.

Epic's Recommended Use Cases

Epic mentions examples of what makes a mesh a good candidate for nanite:

 

  • Contains many triangles, or has triangles that will be very small on screen

  • Has many instances in the scene

  • Acts as a major occluder of other Nanite geometry

  • Casts shadows using Virtual Shadow Maps

  • If you want to tessellate a mesh

  • If a specific mesh takes up a lot of disk space

 

Useful to keep in mind as we dive into our testing.

Memory Size on Disk Testing

Let's start by looking at how the Nanite data type affects the build size on disk.

 

I created a sample project using the fps controller in a mostly empty environment. For our control group, I’ve packaged the empty game which shows a file size of 745mb.

2024-12-15 14_37_20-Nanite Limitations - Google Docs — Mozilla Firefox.png

Then I added 50 unique low poly meshes to the scene, grabbed from Quixel Bridge. I deleted their textures/materials and dropped them in the scene, and then packaged the project again.

2024-12-15 14_37_57-Nanite Limitations - Google Docs — Mozilla Firefox.png

Next I turned all of the meshes into nanite and re-packaged again. Following that, I swapped out the low poly meshes for their high poly version on Quixel Bridge and repeated the test, packaging the project when everything was nanite and when everything was non-nanite. 

​

My final values were recorded below:

2024-12-15 14_42_33-Nanite Limitations - Google Docs — Mozilla Firefox.png

From this we can see that Nanite does in fact change how the mesh data is stored, and generally compresses it better as poly count increases.

Triangle Count Testing

In order to test the cost of nanite triangles vs non-nanite triangles, I created a test scene that contained 1080 meshes, with 81 unique materials.

2024-12-15 14_46_48-Nanite Limitations - Google Docs — Mozilla Firefox.png

The meshes were locked to certain triangle counts like 1k, 4k, 16k, etc. I tested the performance of the scene when everything was non-nanite, everything was nanite, and when only a single object was nanite. Data below:

2024-12-15 14_48_25-Nanite Limitations - Google Docs — Mozilla Firefox.png

From the data, we can draw some interesting conclusions.

​

  • Turning on nanite at all, even for a single object, has a large overhead cost of ~.8ms. This seems to be the cost of adding the nanite buffer and reading/writing to it. So it makes sense that if you want to use nanite, you try to turn it on for everything you can to justify the overhead cost.

  • Nanite in general is less stable; I noticed more frequent dips and hitches in performance when it was turned on.

  • It seems that nanite “breaks even” on performance when the average mesh is around 6k triangles. If the average triangle count is above 6k, nanite meshes start to become cheaper than non-nanite meshes. When the average triangle count is below 6k, nanite is more expensive than non-nanite due to the initial overhead cost.

Foliage Testing

Foliage is an interesting candidate for nanite.

 

On one hand, we typically want a lot of instanced foliage meshes in our games, and nanite likes to draw many instances at once.

​

On the other hand, foliage meshes tend to cover very little screen space and don't do a good job occluding other objects behind them. Since nanite prefers to have meshes fully occluding other meshes to save on cost, foliage tends to be an expensive asset for nanite to process and leads to a lot of overdraw.

 

Furthermore, foliage is traditionally made with masked materials, using leaf cards to represent the geometry and save on poly count. In the past we haven't seen much of a performance difference between Opaque and Masked materials, but that changes with nanite. Masked materials on nanite meshes are much more expensive than opaque materials, as you'll see down below.

​

Below is my foliage test. I created an empty landscape, and painted 3.04k low-poly grass meshes (4 polygons each) on it in a very dense space in front of the camera. I tested the difference between Opaque and Masked materials on them, then toggled nanite and looked at the opaque/masked materials again. Then I swapped out the grass mesh with a high-poly version of the grass (29.4k polygons each) and repeated the test.

2024-12-15 15_07_31-Nanite Limitations - Google Docs — Mozilla Firefox.png

Wow! Lots of interesting takeaways here.

​

First, for non-nanite meshes we see that low poly is about the same for both Masked (7.89ms) and Opaque (7.77ms). The difference becomes huge when we look at the high poly versions though, with Masked (38.93ms) and Opaque (29.16) having a 10ms difference.

​

The nanite low-poly meshes for Opaque and Masked perform about the same as each other. And both of them perform a little worse than their non-nanite counterparts, but the difference is only ~0.2ms which isn't too bad.

​

The real improvement comes from looking at the high-poly nanite meshes though. High poly Opaque is by far the best, having a cost of only 8.87ms. This is ~1ms higher than the cheapest option for foliage, which is the non-nanite low poly opaque (7.77ms). Considering the overhead cost to simply turn on nanite can get up to ~.8ms, this makes it very cheap to use high poly foliage meshes as long as they are opaque.

​

On the Masked nanite side of things, we see (11.03ms) which is about 2ms higher than its Opaque version. For games trying to hit 60 fps (16.66ms), this is a tough pill to swallow and is a high enough cost to justify re-modeling the foliage to become opaque instead of masked. It's still a huge improvement over both of the non-nanite high poly options (that were both 29ms+) though.

Landscape Testing

My landscape test isn't as robust as the others. I simply created a new landscape, sculpted a few hills on it, and then looked at the performance of it through a static camera. Then I toggled nanite on it, and looked at the performance again.

​

Normal landscape: 7.35ms (136.05 fps)

Nanite landscape: 8.26ms (121 fps)

​

In my case, the nanite landscape was about ~.9ms higher cost than non-nanite, which falls in line with the typical overhead cost of using the nanite buffer. If you are already using nanite for other assets, then it seems like landscape is a great candidate to opt into it as well. And if you're not already using nanite, then you probably don't want to opt into nanite just for landscape alone.

Tessellation Testing

With the introduction of nanite, Unreal removed traditional tessellation and locked it behind nanite meshes only. So now, if you want to use displacement then you have to opt into nanite. That's not super great due to the overhead cost of nanite, but let's see how the performance of new tessellation holds up.

​

I tested it on both a cube mesh and landscape.

2024-12-15 18_12_08-Nanite Limitations - Google Docs — Mozilla Firefox.png

Here we see that enabling nanite once again costs ~1ms. Enabling tessellation adds another .5-.8ms it seems.

 

Now, notably Epic mentions that the command r.Nanite.DicingRate can be used to limit the amount of tessellation happening on a mesh. And this is true to an extent: I can visually see the tessellation triangles getting larger as I set the dicing rate higher. The default dicing rate is 2; if you reduce it to 1 then the mesh will be tessellated more, and if you increase it to 3 then the mesh will be tessellated less.

 

Sadly, even when I have the dicing rate set to 20, I didn't get any performance gains from it. So I'm not quite sure what the point of it is, unless Epic hasn't implemented it fully yet.

Final Thoughts

So what's the takeaways here?

​

In general, I don't find nanite to be very useful. If you already have a good optimized pipeline for asset development, then nanite isn't going to give you any performance gains. It seems most targeted to the crowd of people who don't optimize their assets, and mitigating the damage of meshes that don't have LODs/texture maps to showcase detail.

​

That said, I see a few beneficial scenarios to use nanite:

​​

  • If you are really hurting on disk space. Nanite does allow you to compress your meshes on disk better, and so you could author them to be higher poly and forego using a normal map. There are plenty of other areas to reduce wasteful memory though, so this seems like a small convenience.

  • Foliage. As stated above, foliage commonly has thousands of instanced meshes in a level, and good looking foliage can have thousands of triangles per mesh. Using nanite here can really reduce your Draw thread costs, provided that the foliage was modeled correctly. You'll want to switch over to using opaque materials on your foliage for the full benefit, not masked, which can be a headache for some teams to implement.

  • Tessellation. Considering Epic removed all other forms of tessellation, you need to turn on nanite if you want to use it now.​​​​​

  • Instagram
  • Vimeo
bottom of page