Breaking the draw-call barrier

[UPDATE: In a bare-bones test where draw calls were the only factor (no game logic, physics, etc), the frame rate was steady at 30fps up to about 60-65 draw calls, at which point it started decline slowly with additional draw calls.]

I’m starting this thread to see if we can get some good open discussion going about how to work past the draw-call barrier in Unity iPhone.

I’ve found that Unity iPhone is incredible at rendering surprisingly high-poly scenes - provided you keep drawcalls really low.

I’ve also found that no matter how low-poly your scene is, if you go over about 25-30 draw calls, the fps drops in half (15fps). That isn’t too bad for some game types, but ones that depend on the user tapping or dragging objects around with their finger, it becomes much more noticeable. As such, for some games it can be a serious limiting factor. Even a handful of status indicators on-screen can push you over the line.

There seems to be a lot of overhead with drawcalls, because I’ve seen some other (low-poly) 3D games on iPhone that seemed to have more than 25 or 30 independent objects that ran at 30fps.

To overcome this barrier and allow more than 25-30 independent objects without incurring the speed penalty, I’ve tried parenting objects, then applying CombineChildren at runtime to combine them into one drawable object. Then I tried removing the script again to modify the child objects and then re-apply the script after modifying them. This did not appear to work.

Someone else had once suggested some sort of procedural mesh solution. But I wasn’t 100% sure how something like that could be implemented to overcome this barrier without being an absolute headache to work with from the content creation and management side.

So I’m hoping that someone who is more familiar with Unity than I am may have some good input on this and have some ideas how this can be overcome. And with that, let the brainstorming begin!

Are you making sure that seperate meshes share materials and especially share textures. Texture state changes are very expensive on the iPhone.

Yes, almost every object on-screen shares the same texture and material. I basically have 1 material for the background, 1 for everything else except text (when there is text).

Interesting thread. I’ve been fighting 2 weeks now getting my little game “Bubble Bang” to work at least somewhat playable on the iPhone. It uses lots of rigidbody bubbles and I came into this “lots of draw calls kills framerate” issue too.

What I did so far is lock the rotation of each rigidbody. That saved a bit of time on each bubble. Additionally I also tweaked the physics time and made the bubbles 2d planes instead of 3d objects.

Further, what Joe also suggested, I tried to understand how I can have only a very few materials at all in the game to avoid hefty render state changes. I’m using for the bubbles 6 materials at all, but from the docs I’m not sure if it is requried to add the materials I created to the shared material list or if this is done automatically.

@Joe: As you mentioned it, is there some extra step involved to have like a few materials be shared materials? Can I assign them manually? I thought from the docs that once a material is created it is automatically added to the shared materials. Is that right?

Brady said:

I found that turning the script on/off or running the combine process separately caused a real slowdown on the iPhone. It was better to have (in my case) an additional 9 draw calls.

Would be interested to see if you have continued to work on this and maybe found a way around it ?

Our occlusion demo runs at 20-30 fps depending on the view point with 50-60 draw calls on average. I am not sure on what you are doing different. But are you sure it is related to rendering performance and not some scripts or some other tasks?

Number of different mesh object or different Game Object that is holding mesh data for rendering mean the Draw call.

So, Brady is doing a process that will combine the different mesh data in a single game object that will also make a big box for clipping test. I did this kind of task in a different project. This is only applicable for static mesh data and mind it will make box bigger. So, most of the case it will not rejected for culling.

Let’s consider a situation: I have one mat which have a big texture 1024x1024. If I mapped UV in such a way that 8 different mesh data sharing this texture. So, this will reduce texture swap. But not draw call. In that case combining the mesh in a single game object and transform vertex to corresponding world position will reduce texture swap and draw call.

Moreover, I have a question can one help me? If I have a mat say name “tree”. If I have 10 different tree and in 10 different world position which are using this mat, how many texture swap will happen for this? 1 or 10?

Strange, I don’t get anywhere near 20-30fps in the areas with 50-60 drawcalls. I get 10-15 standing at the very start point (which has about 60 some odd drawcalls). I only jump up in the 30fps neighborhood if I’m facing a wall at close range so that there is not much in the frame.

I just did another test that was completely bare-bones - nothing but individual quads (so 2 tris per drawcall) all using the same texture, and it stayed steady at 30fps up until I hit 60-65 drawcalls, and then it started to waver. At 70 drawcalls, I got around 20-28fps. This test scene had zero colliders, zero physics, no dynamic lighting, etc, so it was pretty much a test of only drawcalls. But my “real world” tests, which include a small bit of physics (4-8 rigid bodies) and a bit of game logic, put the drop-off point around 25-35 drawcalls. This is still with a very low-poly scene and only 2 textures in memory from which all objects are drawn.

My tests so far have been puzzle-type games so there’s pretty much nothing out of frame, so frustum and occlusion culling don’t play a role here.

Strange, yes, me neither gets these high framerates.

Hey Joe, if you like, I can send in my project folder! :slight_smile:

I also seem to be having this issue. I’ve got a scene with ~ 15-25 draw calls. I’ve tried two models, one higher poly terrain and one low poly terrain. My scene had 3000 - 4000 polys in view with the low poly terrain and 8000-11000 with the high poly terrain. Even with the low poly terrain my frame rate only jumped to 20-30 fps when looking out at the skybox from the edge of my terrain model OR I picked up all the pick-ups in the scene, which reduces the draw calls to 15.

On the flip side, I’ve noticed that if you can keep the number of draw calls really really low, you can push a TON of polygons. Way more than the Unity docs recommend. With only 4 draw calls, I can push two 8,000+ poly meshes (16,000 polys) and get 15-20fps. Not all polys are in frame at once, but both meshes are. I can get a solid 30fps with a 8,000 poly mesh and only 3 draw calls.