Unity UI Performance tips - Sharing my findings

Greetings,

so after a day of doing research on Unity’s UI, it’s not so obvious pitfalls, quircks, dos and donts etc. I’ve compiled a list taken from misc Unity optimization tip articles and videos, mostly related to UI. We just starting a brand new smaller scale project and I thought it would be a good chance, fairly risk free to try Unity UI for a bit, as we’ve made all our previous games with NGUI and while it got the job done, we had a lot of beef with it. I thought Unity’s UI would easily be faster since it’s better integrated to Unity with a team of optimizers behind it, but it didn’t turn out to be that simple. A lot of the decisions in the components and systems of the UI (e.g Layouts) are built in a way that it’s very easy to shoot yourself in the foot with it. Hidden performance costs riddled across the codebase (see linked video). Documentation (except the optimization ‘articles’) was pretty lacking, mostly telling what the difference between system A and B is but not giving hints/advice on when to use when or what the cost of using a system over another in certain circumstances etc (e.g. Canvas Rendering modes). A lot of the optimization tips tells you to not do what the tutorials tell you to do (e.g. avoid Layouts etc).

This stuff goes beyond ‘make your game correct first then fast later’ cause you could quickly and easily build up bad habits and practices and end up later with a that’s very hard if not impossible to optimize. Profile/optimize more frequently Imo to avoid falling into that at a later stage in your project. A lot of this is structural/archtectrual stuff that add up, not minor bit twiddling and tiny performance hacks that you could slap in whenver you want. So it doesn’t hurt to start off correct from the beginning.

Anyways here’s my list, please feel free to correct me if I was wrong or inaccurate on of these points (It’s also why I’m sharing it here).

  • Limit the usage of ‘Best Fit’ and ‘Rich Text’ in Text components

  • Don’t use dyanmic, use bitmap/static fonts if possible

  • Limit the usage of Outline/Shadow components on Text game objects. Bake the effect in the bitmap font instead

  • Stick to ‘simple’ sprites if possible. Avoid large ‘tiled’ sprites

  • Flatten out the transform hierarchy. The more nesting the more transform calculations needed to position children correctly

  • Uncheck “Pixel Perfect” in the Canvas settings. Big performance hitter.

  • Screen Space Overlay UI is slightly faster than Screen Space Camera. In Overlay the UI is drawn on top of the screen immediately avoiding all the culling and sorting that Camera space has to go through. (The performance hit shouldn’t be that critical, and Screen Space Camera is convenient though, so maybe go with Camera and see if it causes you issues down the line)

  • Avoid stacking UI images/text on top of each other to best of your ability. This causes more overdraw and will slow things down

  • Subdivide your UI to multiple canvases based on what’s static vs dynamically moving. Changing the position/rotation/scale/sprite/text of a UI element in a canvas causes a rebuild of the canvas’ mesh for all its children. So having a single Canvas for the entire UI of your game is not a great idea. Divide it into logical groups that update/change together. e.g. Maybe each menu/screen/subscreen/popup has its own Canvas, maybe the hud top has a canvas vs the hud’s bottom, etc.

  • General tip: Avoid Camera.main calls as it does Object.FindObjectWithTag every time it’s accessed! Have a direct reference to the camera you’re using instead.

  • Prefer enabling/disabling the canvas itself instead of the entire gameobject. Avoiding OnEnable/OnDisable callbacks on the entire hierarchy which could be expensive.

  • Avoid LayoutGroups. Roll out your own (see linked video for more details)

  • Updated pooled objects properties first before enabling it when borrowing an object from the pool. This avoids unnecessary dirtying of the object’s hierarchy

  • With ScrollRects with many items, attach a Canvas (with no Pixel Perfect) to the scroll rect so that scrolling the items won’t dirty out the elements in the outer canvases

  • Pool the items in the view. enable/disable elements as they get in/out of view

  • Clamp the velocity of the scrollview so that you don’t end up with very small deltas making very small changes to your elements without it being apprently changing on screen

  • Turn off Raycast Target for static or non-interactive elements. This reduces the number of UI objects the collision/click/input raycast checker has to go through to determine what UI elements should receive events

  • Take out GraphicsRaycaster components from Canvases that don’t require input events

  • Pack your sprites together to reduce drawcalls. Use Texture Packer or Unity’s atlas/packer systems

// Questions/Uncertain:

  • Does moving the Canvas itself trigger a rebuild/makes it dirty, or does that only apply when moving one of its child elements? From what I’ve read so far it seems that it doesn’t, only when you change the Canvas’s children.
  • Still not a 100% which is faster/better, moving elements by their local/position or anchoredPosition?

// Links:

Thanks

26 Likes

Very helpful tips, thank you!

Is using the unity UI better than direct drawing texture to screen?

You mean rendering each texture/image individually without batching them into a single draw call? I’d imagine that’s more expensive, more draw calls.

Thx for this overview of tips !!!

Thanks for the tips.
One question: if you move an object, but to the same position (for example some off-screen indicator of an object that is not currently moving and player camera is not moving/rotating as well) - is the canvas going to get dirty or not?

Thanks for Sharing:

Re: “Subdivide your UI to multiple canvases based on what’s static vs dynamically moving. Changing the position/rotation/scale/sprite/text of a UI element in a canvas causes a rebuild of the canvas’ mesh for all its children. So having a single Canvas for the entire UI of your game is not a great idea. Divide it into logical groups that update/change together. e.g. Maybe each menu/screen/subscreen/popup has its own Canvas, maybe the hud top has a canvas vs the hud’s bottom, et”

I knew this recently too, but doubt how to use. Because using different canvas will break the graphic batch (more batch needed).

So is it better to keep dynamic & static canvases first and breakdown to more if the performance isn’t good?

1 Like

one of the best solution in my opinion is that create images with those static texts(that won’t changed at all).in this way unity won’t need to relayout graphics for this particular text.
Example:
if you have a button with a text with it , you can create this button at one single image and remove text from unity.

1 Like

That’s fine until you need to localise it. Then you’ve got the different problem of creating a new set of UI graphics per language.

1 Like

Changing the text value in the text component doesn’t dirty the canvas. But other properties like color and stuff still triggers a repaint. Not sure why though.
But its still better to make a static text into an image cuz text components are not batched with the images.

what does “dirty” mean?

Dirty means nasty. Just kidding. In this context dirty means that (parts of) the UI has changed and needs to be rebuild. But its also used for objects in editor and scenes. So it generally means that something has changed and some further action is neccessary. It’s kind of a flag which can be queried. For example when you changed the scene in the editor it is flagged dirty and when you attempt to close the editor it checks the scene dirty flag. If it is dirty the Editor asks you wether you want to save the changes or not. When you save the scene manually before closing, the dirty flag is removed and the Editor can close directly.

1 Like