Hello ladies and gentlemen of the Unity forums. I could really use your help.
I am working on a project where we have to draw several hundred little, opaque pieces of geometry into the scene that have to stay as individual game objects. Of course, that means the Unity engine is having to deal with several hundred little draw calls and on iOS that many draw calls can be a death for the game. Thankfully, for the moment, we’ve got the whole game running at a healthy clip, but we desperately want to try and optimize this part of the code if only to give us a little more headroom in case something else in our game starts to slow down the render pipeline.
Now, the ideal solution for this problem would be geometric instancing, since these several hundred things are all basically the same except for differences in transformation, rotation, and scale. But instancing is not something the Unity engine supports, especially on the iOS platform.
So, I’m basically looking for any suggestions or tricks anyone can give that can help us cut down on the draw calls in the game.
Here’s some other basic information for you to give you some context
- The little things on screen are fruits and vegetables
- There can be up to 600 fruits and/or vegetables on the screen at once
- There may up to eight varieties of fruits and/or vegetables on screen at once. Each variety is defined by a single mesh and a single material.
- These little things need to be able to change their position/rotation/scale at any arbitrary time
- StaticBatching isn’t an options since the things need to be able to move an animate
- We tried to use MeshCombine, but weren’t able to get any significant increase in performance since we basically needed to recombined the meshes every frame
Again, as I said, this problem has an ideal solution in geometric instancing. But, since that isn’t an option in the Unity engine at the moment, I’m hoping someone can provide a tip, trick, or way of thinking that can help me cut back on the number of draw calls for these blasted fruits and vegetables.
If you can get all your fruit/veg textures into one texture (atlas) and single material (using the atlas) can be used to render all the meshes, and the meshes each have 300 vertices or less, and a single camera is rendering them all, then Dynamic Batching should reduce it down to a single draw call! Well perhaps depending on the total number of vertices. I don’t know what the threshold is.
Thank you both, but I’m afraid neither is very helpful. To sama.van, we’ve done all that’s listed under iOS Specific Optimizations and have been profiling the game as much as we can. The issue is the size of the rendering and draw call load, and there’s no advice in the iOS optimization page for really dealing with that.
And to anymads, unfortunately the dynamic batcher isn’t optimizing to such an extent. This is partly because, at the game designers request, not all the fruit is the same scale. Even if we made it so all fruits end up the exact same size, the fruits do have to grow to that size. Thus, because of this, dynamic batching can only save us the frames in very specific situations that, right now, are game isn’t hitting.
So, again, thank you for your input from both of you, but these are avenues that have already been explored.
Unity supports geometry instancing. However, the actual PowerVR GPU on the iPhone does not support geometry instancing. Your issue isn’t a unity thing, it’s a OpenGL ES thing.
Dynamic batching is probably your easiest route to go. It is true you can’t scale objects form I understand. But you should have designed your game around this technicality from the beginning, having the fruit get closer and farther from the camera to change their size rather than scale them. in the future you need to brainstorm all potential technical pitfalls before designing a game and then do tests to make sure you can work with them or around them.
However there are still options available for you. You can use particle geometry instancing. Make a particle system for each separate mesh, and set the particle system to use that mesh as it’s geometry. You can then manually move the particles around with GetParticle and SetParticle. The entire particle system will, each frame, combine all instances of the geometry into a single mesh and submit it to the GPU in a single drawcall ( I think thats what it does internally ).
Your other option is to make use of MeshBaker http://forum.unity3d.com/threads/159258-Mesh-Baker-by-Digital-Opus-RELEASED
Attach each of your objects to a bone, and make them a skinnedMeshRender, then combine them all together with MeshBaker, and animate the bones around. This method may be the most intricate, but it will probably give you the best performance.
Each of these methods will require a bit of changing of your games internal structure…
To give an update to this, I should thank andymads for making me look back at Dynamic batching again. I knew it should have been working, but I thought because of the differences in scales and what not it wasn’t. I ran several tests, but found that the carrots I threw into the scene with the edtior were batching despite having numerous different scales. To say the least, I was confused.
I then happened upon another thread, and it enlightened me to why dynamic batching was failing. I’m now going to share this here for any that would come after.
The issue was actually in the materials. Now, as most unity developers will tell you, if you modify a material on a model it becomes instanced. It becomes a unique copy, and this can destroy dynamic batching. Now, I wasn’t modifying the material on the carrots at all. But I was reading something from them. Yes, even cracking open the renderer.material to get a value from it or to even copy it into a local material buffer makes unity treat it like an instanced material. It’s like touching the sides in the game Operation or something.
When I went back through my code, I found one moment where I was copying the material into a local script parameter, and that was making most if not all the fruits have instanced materials. By switching that statement to use “sharedMaterial” instead, Dynamic batching was able to kick back in and, as andymads suggested, was able to cull everything down to one draw call.
So, again, if you are having this issue make sure your not even touching renderer.material. Even looking at it will make it an instanced variable.
Kind of reminds me of Schrodinger’s cat in a way.
Still, thank you everyone for your assistance.
I’d like to see the confused look on Schrodinger’s face when his cat had kittens while in the box! 
Good to know you got it working. I’m creating a 3d maze (see sig) and when I broke it down, found it only used 7 unique sections. I position each section model to create a connected random maze. Each maze could have hundreds of these sections. I was banking that some type of batching or instancing would help me.