Batching draw calls

I’ve run into a brick wall.

I’ve got 80 gems of different sizes and colours.

Rendering on my iPod touch I get < 10fps. unsurprising, Unity reports 80+ draw calls are being made.

Doc says that each material means another draw call, and fiddling a material’s properties creates a clone of that material. So this code in the Update() of each gem:

			T_gem.renderer.material.color = C_gem; 
			T_gem.renderer.material.SetFloat( "_Glow", glowIntensity / 2f );

was doing that.

So, I eliminate this by combining RGB colour and glow into a single RGBA and adding this as a color32 to each vertex:

	void LateUpdate()
	{
		if( glowIntensity < 0.03f )
			return;
		
			Color C_gem = color; // doGlow ? Color.Lerp( color, Color.grey, glowIntensity / 2f ) : color;
		
			Mesh mesh = T_gem.GetComponent<MeshFilter>().mesh;
			
			Color32[] colors32 = new Color32[ mesh.vertices.Length ];
			
			C_gem.a = glowIntensity;
			
			for (int i = 0; i < mesh.vertices.Length; i++)
				// http://docs.unity3d.com/Documentation/ScriptReference/Color32-operator_Color.html
				//   Color32 can be implicitly converted to and from Color.
				colors32[ i ] = C_gem; 
			
			mesh.colors32 = colors32;
	}

Note that I’m doing it in LateUpdate. This means that all of the meshes get recalculated AFTER this frame has drawn, but BEFORE the next frame has drawn.

Of course I have to fiddle the shader to no longer receive colour and glow as properties, which I’ve done successfully:

Shader "FX/Piamond"
{
	Properties {
//		_Color ("Color", Color) = (1,1,1,1)
//		_Glow("Glow",Range(0,1)) = 0.5
		_ReflectTex ("Reflection Texture", Cube) = "dummy.jpg" {
			TexGen CubeReflect
		}
		_RefractTex ("Refraction Texture", Cube) = "dummy.jpg" {
			TexGen CubeReflect
		}
	}
	
	SubShader {
		Tags {
			"Queue" = "Transparent"
		}
		
		// if I don't have all of these, it goes wonky
		BindChannels {
		   Bind "Vertex", vertex
		   Bind "Normal", normal
		   Bind "Color", color
		   Bind "texcoord", texcoord0
		   Bind "texcoord1", texcoord
		}
                 .... etc

Still 80+ draw calls!

Now each gem has about 600 vertices: Unity - Manual: Draw call batching says something about a magic limit of 900 that reduces to 600 or 300 if vertices have colour / texture components. I don’t understand that. But anyway, I have tried replacing them with cubes, and exactly the same thing happens.

Still 80+ draw calls!

Now I’m not 100% sure that all these clones are sharing the same material; only reason I’m not sure if that there is a property called sharedMaterial, and I’m not using it. So I try and use it:

	public Material gemMaterial;
	
	// Use this for initialization
	void Start () 
	{
		for( int noteIndex = 0; noteIndex < numNotes; noteIndex++ )
		{
			Transform T_Clone = (Transform)Instantiate( T_GemContainer_Prefab, Vector3.zero, Quaternion.identity );
			
			GemContainer G = allGems[ noteIndex ] = T_Clone.transform.GetComponent< GemContainer >( );
			
			G.SetMaterial( gemMaterial );

and

	public void SetMaterial( Material m )
	{
		T_gem.renderer.sharedMaterial = m;
		//T_gem.renderer.material = null;
	}

Still doesn’t work. Still 80+ draw calls!!!

Dynamic Batching and Instantiating - Questions & Answers - Unity Discussions this question here has a very good answer, which says this only works if all of the clones have the same transform.scale

So I try removing the line of code that changes the transform.scale for a clone.

		transform.localScale = uberSize * animSize * ( 1f + gemSize ) * Vector3.one;

Still 80+ draw calls!!!

Please could someone have a look at this?

i think all objects must be identical in material to be patched… so if you are changing the material color, this might be the cause…
i think it’s better to have fixed amount of different colors…

in my case, if i want to have multiple models patched, i would include all these models to share same texture file…
each has it’s own part in the uv… then they get patched… no need to alter the material, i only instantiate the object with the color i need…

Take a look at Dynamic Batching on

Stupid question, is dynamic batching turned on in the player settings?
Are your gems using more than 900 vertex attributes?
What kind of shader does your material use.

If you’re instantiating a prefab and you’re not changing any individual material attributes after, you should already be using the same material on all objects.

The different sizes may be the problem

Make an empty gameObject and drag all your gems into it then go to Standard Assets> Scripts>Utility Scripts and drag the Combine Children Script into the gameobject containing all your gems. This should reduce your draw calls by a good amount.

Thanks for the answers, I have just heavily edited the original post in light of them.

@MangaX, I’ve clarified the original question – I believe I’ve prevented the material from being cloned

@ SchneiderAtvis, I’ve tried replacing them with cubes, so it appears I’m not hitting against this 900 vertex limit. this limit seems weird and wrong to me… it just seems completely arbitrary. Can it be changed?

@fffmalzbier, I don’t understand this: 'Generally, objects should be using the same transform scale. The exception is non-uniform scaled objects; if several objects all have different non-uniform scale then they can still be batched. ’ Does anyone? By common sense this must surely be back to front.

@Intense_Gamer94, I’ve just been looking at this script. Problem is I’m generating these objects at runtime. So I may need to pick the script apart and rework it.

It still isn’t working, despite

  • eliminating any rescaling of the individual gems
  • replacing them with cubes so as to reduce the vertex count massively

What I’m currently thinking is to combine all of the meshes.

EDIT: thanks to the guys on #unity3d, I’ve got a little further

If I replace the shader with a diffuse shader, it does batch successfully
More to the point, if I stick with the gem shader but take out one of the two passes, then it works!

So it looks as though unity can’t batch objects that have multipass shaders.

Now it’s possible to put each pass into its own render queue:

		Pass {
                       Tags { "Queue" = "Transparent+100" }

and that to my mind should work. But it doesn’t! I tried using Geometry queue instead of Transparent but no luck.

EDIT: I’ve now moved onto manually combining the individual meshes http://forum.unity3d.com/threads/208041-Manually-combining-meshes-per-frame