Introduction:
I think it’s well known amongst the Unity community that OnGUI has very bad performance when making games, especially for mobile platforms.
In my personal experience it seems to be the highest CPU drain in my current project while running mobile profiling.
(Profiles taken july 17th)
I ran some profiling on my project back in July and found that GUI.repaint was the highest CPU drain on my PC at 3%.
However I’m much more concerned about the OnGUI performance for mobile platforms. And on my Samsung Galaxy Nexus the profiler break down was like this on average:
Camera.Render = 44%
GUI.Repaint = 17% (this was biggest one on PC at like 3%)
Physics.Simulate = 15%
MeshSkinning.Update = 6%
Camera render being so high is sort of to be expected since we are trying to really push the limits and innovate as much as possible for what newer devices are capable of. But GUI.Repaint at 17% the seconds biggest CPU drain? This seems unacceptable to me.
So OnGUI is either the highest CPU Drain or the second highest CPU drain on both PC and my phone last time I checked.
So I’ve been meaning to investigate this a little bit more since the Unity documentation always seems to be completely void of any useful information I’m wondering about. Finally got around to running a few tests today.
The Test:
All tests will be run in my current game project in the exact same location and camera angle where you start the first level. This area has a large smoke particle effect for the crash site, and I also added 15 armor pieces to the camera view which all have particle systems, so this test should be a little more stressful for mobile platforms.
Create 3 int variables to track how many times OnGUI, Update, and FixedUpdate are called by incrementing the individual counters for each of those function calls. Counters will be displayed in OnGUI.
Will be testing in the Unity Editor, PC, and 2 Android devices.
Each test will attempt to accurately measure the number of OnGUI calls in relation to the Update and FixedUpdate calls. Running each test twice for each platform and taking a screenshot when the OnGUI counter reaches 1000. Then converting all the counters to what they would be if OnGUI was exactly 1000 in the screenshot (hard to take screenshot exactly right since OnGUI is called an insane amount of times per second). Averaging results of the 2 tests per platform or device.
Will be running majority of tests with Application.framerate set to 30 to mimic a mobile build. Also doing a round of tests with Application.framerate of 10000 for PC, and Unity Editor. Using default Unity settings for FixedUpdate. Fixed Timestep = 0.02 and Time Scale = 1 (50 FixedUpdate calls per second).
Test Results:
Unity Editor with PC settings:
Test 1
- Target Frame Rate = 30
- Actual Frame Rate = 30
- OnGUI = 1000
- Update = 493
- FixedUpdate = 816
- Result = 61 OnGUI calls per second
Test 2
- Target Frame Rate = 10,000
- Actual Frame Rate = 304
- OnGUI = 1000
- Update = 499
- FixedUpdate = 82
- Result = 610 OnGUI calls per second
PC Build:
Test 1
- Target Frame Rate = 30
- Actual Frame Rate = 30
- OnGUI = 1000
- Update = 494
- FixedUpdate = 818
- Result = 61 OnGUI calls per second
Test 2
- Target Frame Rate = 10,000
- Actual Frame Rate = 950
- OnGUI = 1000
- Update = 494
- FixedUpdate = 26
- Result = 1923 OnGUI calls per second (had to take screenshots @ 10,000 OnGUI and convert values since so fast)
ANDROID TEST (Samsung Galaxy S4)
Test 1
- Target Frame Rate = 30
- Actual Frame Rate = 29
- OnGUI = 1000
- Update = 484
- FixedUpdate = 842
- Result = 59 OnGUI calls per second
ANDROID TEST (Samsung Galaxy Nexus)
Test 1
- Target Frame Rate = 30
- Actual Frame Rate = 26
- OnGUI = 1000
- Update = 488
- FixedUpdate = 922
- Result = 54 OnGUI calls per second
One final test to see what happens when adding the OnGUI counter to all the enemies in one of my scenes… (all previous test are just one script’s calls).
Final PC Tests:
Test 1
- Target Frame Rate = 30
- Actual Frame Rate = 30
- OnGUI = 1000
- Update = 8
- FixedUpdate = 13
- Result = 3943 OnGUI calls per second (had to take screenshots @ 100,000 OnGUI and convert values since so fast)
Test 2
- Target Frame Rate = 10,000
- Actual Frame Rate = 894
- OnGUI = 1000
- Update = 9
- FixedUpdate = .48
- Result = 105,144 OnGUI calls per second (had to take screenshots @ 1,000,000 OnGUI and convert values since so fast)**
Test 3 (same as Test 2 except adding OnGUI counter to my biggest OnGUI script in scene)
- Target Frame Rate = 10,000
- Actual Frame Rate = 902
- OnGUI = 1000
- Update = 8
- FixedUpdate = .46
- Result = 107,691 OnGUI calls per second (had to take screenshots @ 1,000,000 OnGUI and convert values since so fast)
Conclusion:
So it seems that OnGUI is called significantly more than either Update or FixedUpdate in all scenarios, even when only incrementing the OnGUI count on 1 single script.
The lowest OnGUI calls per second I was able to achieve was 54 with the Samsung Galaxy Nexus aiming for 30 frames per second. This still was slightly over 2 OnGUI calls for every Update.
When changing the target frame rate from 30 to 10,000 the results become much worst. With only one OnGUI script the PC recorded an astounding 1923 OnGUI calls per second. This was also over twice the amount of Update calls, and over 38 calls per FixedUpdate.
When adding the OnGUI counter to increment on the original script in addition to all the enemies in one of my scenes the results became quite frightening even at 30 FPS. 3949 OnGUI calls even at a measly 30 FPS. That is 125 calls per Update frame, and 77 calls per FixedUpdate.
When removing the FPS limit of 30 and letting the PC really fly OnGUI becomes total beast mode. In my final test while adding the OnGUI increment to one additional very large OnGUI script in my scene it recorded a ridiculous 107,691 OnGUI calls per second. Once again 125 calls per Update but a horrendous 2174 calls per Fixed Update.
Final Thoughts:
It is now clear to me why I’ve seen so many people warning others to never use OnGUI. If it is allowed even slight room to be free things can get extremely ugly.
It is unclear to me why OnGUI is called over 2 times as much as Update even in my best scenario.
It seems strange that OnGUI is called so many times when it seems like it should be called in sync with Update ideally. What is the point of it updating faster than the frame rate?
When using it on multiple enemies throughout a scene for things like floating combat text and their health bars it becomes quite ridiculous how many times it is called. I have seen suggestions to disable Game Objects to avoid OnGUI if they are out of range. That makes a whole lot of sense when I’m seeing 125 OnGUI calls per Update even at 30 FPS.
The scary thing is that I didn’t even add the OnGUI counter to many other objects in my scenes that include it! Many of my items like health potions include OnGUI for floating text when you pick it up. So the true OnGUI calls per Update and FixedUpdate are even worse than this report.
After looking at all this data it is no surprise that OnGUI is the biggest CPU hog on PC, and takes up over 17% on the Samsung Galaxy Nexus. That profiler test was taken in July so it’s hard to say how high it is now with many optimizations but also many additions to the project.
Even though OnGUI looks to be horribly inefficient compared to Update and FixedUpdate if used carefully and in moderation it seems that it is useable for even mobile devices.
It seems the people saying it was impossible to use OnGUI effectively probably did not do things to limit the frame rate to 30-60. I would also strongly advise people to use very efficient logic in OnGUI. For example if you are using a for loop in OnGUI you should be extra careful to make sure it only runs when absolutely necessary like once per user action not every OnGUI call. I know this is programming 101 but I’ve seen people struggling with questions while doing stuff like for loops every OnGUI call. Any inefficient code will be greatly multiplied in OnGUI due to it being called so many more times than Update or FixedUpdate!
The majority of the OnGUI logic should be preset you should have variables for all the positions, sizes, etc. Do not calculate these in OnGUI!
OnGUI is definitely an ugly beast of a performance hog, but if used carefully it is very convenient to set up certain displays. Really hoping the new GUI update in future Unity versions will greatly improve the performance though. It’s quite shocking how slow OnGUI is currently.