Avoiding Performance Cost of OnGUI

The Unity profiler revealed recently that Pawns for iPhone was spending 5% of its time in a UnityGUI script that I didn’t need any more. Removing it from my scene was easy enough, but it got me thinking. The script looked something like this:

void OnGUI()
{
   if (showDialogBox)
   {
      // lots of code that never executed
   }
}

Not much going on here! So it appeared that just having OnGUI in a script was expensive. This was worth knowing because I still have three other OnGUI scripts left in the scene. Each of them exits immediately if their dialog box is not showing. So in theory they should cost nothing during actual gameplay. But the profiler told a different story:

It turns out that there are some major setup costs associated with OnGUI. GUI.Repaint is listed as taking over 65% of the CPU! Of that, most of the time is spent in GUIUtility.BeginGUI. Note that almost no time at all is spent in my three OnGUI methods (TutorialController.OnGUI, DlgOKCancel.OnGUI, and DlgPuzzleFooter.OnGUI.) So the early exit clause was working, but the damage was done before OnGUI was even called.

To verify this I disabled the three objects in the inspector while the scene was running. The new profiler results are much healthier:

GUI.Repaint has been eliminated. The time is now mostly spent on rendering, physics, and in scripts.

Since disabling the scripts solved the problem, I did the obvious thing, which was to to disable the objects with the OnGUI scripts on them until needed:

footerDlg.gameObject.active = false;

But one trick is needed to reactivate them. Search functions like Find and FindObjectOfType do not return inactive objects. Instead, you must already have a reference to the object you want to activate.

One way is to start the scene with active = true. Your script can then Find the object, store a reference to it, and then set active = false until it is needed. (This is what I did because it worked well with the code I’d already written.)

	void Awake()
	{
		// cache reference to footer dlg
		footerDlg = (DlgPuzzleFooter) GameObject.FindObjectOfType(typeof(DlgPuzzleFooter));
		if ( footerDlg == null )
		{
			Debug.Log("Error: DlgPuzzleFooter object is missing.");
		}
		else
		{
			// deactivate it until needed, for performance
			footerDlg.gameObject.active = false;
		}
        }

A second way is simply to declare a public variable for the inactive object, and then use Unity’s Inspector to set the value at design-time.

A third approach is detailed in a post on UnityAnswers. It keeps a list of every object that gets deactivated.

The fact that OnGUI looks like any other Unity method is misleading. It’s an expensive operation, even if it returns immediately without drawing anything.

UnityGUI itself is not recommended where performance is important, particularly during gameplay on an iPhone or Android device. But it is a convenient way to define menus, dialog boxes, high score lists, and other parts of the user interface. By deactivating OnGUI scripts until they are needed, you can often avoid paying much for that convenience.

15 thoughts on “Avoiding Performance Cost of OnGUI

  1. Pingback: Tweets that mention Avoiding Performance Cost of OnGUI | MindTheCube -- Topsy.com

  2. Pingback: C# events and Unity3d - Everyday 3D

  3. SimTex

    They is a really helpful post, I’m surely gonna use the deactivating trick for our game. It would be better tho, if unity would look at the GUI code, can’t be optimized very well.

  4. Matt Post author

    Glad this tip is helping others.

    I try to remind myself that UnityGUI was written before they branched out into mobile platforms. It’s a huge improvement over Unity 1.x which just had GUITexture/GUIText, and it still works fine on desktops, but is less well suited for platforms where draw calls are at a premium.. My guess is that they will revisit this as part of the 2D toolkit they are apparently working on.

  5. Pingback: C# events and Unity3d « The Joe Lake Blog – joeylakey.com

  6. Matt Post author

    I like that idea (disabling the component that contains the onGui method, rather than the entire game object.) A nice alternative to the options I mentioned in the post. Thanks!

  7. Pingback: Unity 3D Code optimization tips « Scriptocalypse

  8. Aggror

    Thanks for this article. I have been having lots of performance drops since I finished my gui on my android. Now that the GUI gameobject is disabled, it works like a charm. Really tricky stuff that can keep you going for hours if you don’t know it.

  9. Dan Ruscoe

    Thanks, this was really helpful. I ended up adding public properties for the game objects containing each GUI screen to a GUI manager script, which contains methods to enable or disable them.

  10. Seyed Ahmad Parlhid

    If I want display the GUI’s every time on screen , what should am I do ? Should I pay too much cost ?

  11. Matt Post author

    Most people advise against it. Personally, I think it depends on the game. If its an action game where there is a lot of movement and/or split second timing and/or physics, most people consider UnityGUI to be too slow. It will suck frame rate away from the rest of your game. But, if the game is turn-based, or a non-action puzzle game, I think it might be fine. You could write your UI with UnityGUI, and be prepared to rewrite it later if you are having performance problems. You’ll need to test it on lower-end devices at some point.

    Hope this helps.

  12. Pingback: PROJECTS: EVALUATION OF TASK AT HAND | Binoy Mohanty - GAME ENGINEER (Pre-Alpha build)

Leave a Reply

Your email address will not be published.