Vessel
RSS
 

Optimizing with Unity iPhone, the first three things I’ll do…

24 Apr

Optimization is a bit of a black art when you’re dealing with an api or engine where you can’t profile the internals, and unity-iphone is no exception. I’ve collected here a few of tricks I’ve found that have helped me squeeze more performance out of my iphone projects.

1. Do Less, (aka timeslicing)

Lets be honest, it’s really easy to put lots of code in Update() or FixedUpdate() for a behavior, or have it called once a frame. However that means that no matter how fast that code is, it’s going to be  called at a rate of (hopefully) 30fps.

Most of the time, you don’t need to do this. Take for example this pseudo-code of a homing script.

public class MyHoming : MonoBehaviour {

	public void Update() {
		// expensive targetting and not so expensive trajectory calculations in here
		// currently called every frame
	}
}

Consider replacing it with this:

public class MyHoming : MonoBehaviour {

	public float targettingFrequency = 1.2f;
	public float trajectoryFrequency = 0.2f;
	public float trajectoryBlend = 1.0f;
	private Vector3 updatedTrajectory = Vector3.zero;
	private Vector3 currentTrajectory = Vector3.zero;

	public void Start()
	{
		InvokeRepeating("DoTargetting", targettingFrequency*Random.value, targettingFrequency);
		InvokeRepeating("DoTrajectory", trajectoryFrequency*Random.value, trajectoryFrequency);
		DoTargetting();
		DoTrajectory();
	}

	public void DoTargetting() {
		// search for targets here, we don't do it often because it's expensive
	}

	public void DoTrajectory() {
		// trajectory isn't as expensive but it's not something we need to do every frame
	}

	public void Update() {
		// Just interpolate values each frame
		currentTrajectory = SetTrajectory(Vector3.Lerp(currentTrajectory, updatedTrajectory, Time.DeltaTime*trajectoryBlend);
	}
}
}

It looks a lot more complicated, but it’s really not. You’re still doing the same trajectory updates it’s just that they’re now being blended across multiple frames. I can’t stress how much performance can be saved by doing this to all of your update functions. If you haven’t done so already just do it now.

2. Force garbage collection frequently.

If you don’t, eventually there’ll be a huge spike when it does so automatically.
Try dropping this script into an object in your scene:

using UnityEngine;

class GarbageCollectManager : MonoBehaviour {
	public int frameFreq = 30;
	void Update()	{
		if (Time.frameCount % frameFreq == 0)
			System.GC.Collect();
	}
}

It won’t improve your fps, but it will cut down on the occasional spikes that memory management will cause.

3. Triangle & Draw Call counts

  • Stay under 7.5k tris
  • Stay under 20 draw calls

These are often preached as a gospel, I’d say it’s more of a guide to keep an eye on your art performance.

If you go over 7.5k triangles, or over 20 draw calls it’s not the end of the world… you’re just likely to be bottlnecking yourself at submitting your graphics content to the graphics API.

You can batch draw calls by sharing the same material across mutiple objects and also increase this further by marking objects as “static” when they’ll never move.

But! Don’t forget that there are other factors not displayed here… for instance fill rate, when rendering scenes with overdraw and complex shaders.  The best way to track your total graphics performance is still through xcode instruments, and the debugger log with profiling turned on.

Conclusion

While these three are by no means the only tricks to getting unity to perform on an iphone, these ones are my first port of call when things start to slow. I’ve been told about a whole lot of other optimizations, such as managers instead of unity called Update functions, coroutines better than invokes… so forth but haven’t tried them yet.

Anyone got any suggestions, techniques they’ve found saved them some performance that are different from the ones above?

 
 

Tags: , , , , ,

Leave a Reply

 

 
  1. Jaap Kreijkamp

    June 3, 2010 at 10:52 am

    Another clean way to timeslicing is using coroutines. One of the advantages is that you can be more flexible with the slices, so the time between to actions can vary dynamically according to load and/or AI requirements. It also fits AI quite well because one coroutine can wait until the called coroutine is finished, so your normal moving coroutine can call and wait for the fight coroutine when your AI engages enemy.

    Optimising for iPhone is kind of black box work, you try, you measure, you change, you try again. There’s some info to be found about Unity performance on the internet but most you have to find out yourself. I must say I’m quite amazed what’s possible and what you can get away with.

    For complex scenery, try to reuse textures (texturemaps) and shaders so things can be batched. Light sources often result in explosion of draw calls.

    Colliders are hell, but you often need them, especially CharacterControllers eat performance, avoid when possible.

     
  2. Cratesmith

    July 1, 2010 at 9:45 pm

    I agree totally about the lights and shared materials, in my current project it’s getting close to only having two diffuse materials and one additive for the whole scene.

    It’s a good point about coroutines, especially if you vary the times between them repeating dynamically because it reduces the likelihood of a performance spike on intervals if you create a lot of objects at once.

    A few other draw call rules I’d mention (for the 3g at least)
    - Your first light is free, every light after it costs 1 draw call for every object within it’s range.
    - Don’t use the real skybox, that costs you 6 draw calls. Have a textured cube that’s scaled to the far plane instead (1 draw call)
    - Avoid all GUI and GUILayout calls like the plague. Text meshes are much might be more efficient (haven’t tested), but rolling your own text with SpriteManager is ideal.

    A suggestion I’d have for colliders is to stick to boxes and spheres as they’re pretty fast even on the iphone. Capsules are somewhat expensive, but if you want to roll your own replacement for a character collider they’re the best way to go in my opinion. Colliders without rigidbodies are also more efficient, but they only send collision events if they run into an object that has a rigidbody so it’s not always an option.

     
  3. Some optimization experience with Unity3D « WHEN IT'S DONE

    April 1, 2011 at 1:05 pm

    [...] doing any optimization, I referred to Cratesmith‘s great article Optimizing with Unity iPhone, the first three things I’ll do…. And also, the suggestions from MrDrayton and Darknuke on Unity3D forum was very nice and helpful. [...]

     
  4. ByDesign Games

    August 24, 2011 at 7:41 am

    Came across this article only recently. You might want to update the 3rd section with device specific values…

    Times have changed since those values were current; what with the iPhone 3GS, iPhone4 and iPad1 & 2 all having been released since those values were relevant.

     
  5. Cratesmith

    September 10, 2011 at 5:13 pm

    That’s very true,
    I should write a new article about performance on the newer devices when I get the time.

     
  6. Jayson

    June 28, 2012 at 5:03 pm

    Thanks dude, It’s helps me a lot. Keep it up ! btw, if you do have more sample of code optimizations. please give me some more! :D

     
 
Performance Optimization WordPress Plugins by W3 EDGE