Back in 2017, during the Unite Austin presentation, Unity revealed a fully performance oriented approach to programming, namely the Entity Component System (ECS). In there, the show case demonstrated that they could place 100,000 units on screen with logic and graphics and run the code at over 30 FPS. This was really impressive, but what was more impressive was that they started open sourcing a lot of it.
Having looked into the ECS samples I noticed that the way the boids demo was being rendered was using Graphics.DrawMeshInstanced() as you can see down here:
This was really interesting as it showed that a large number of meshes could be drawn with very little overhead compared to having each one be an individual object.
What is Instanced Drawing
Generally speaking, if you wanted to draw a large forest or a number of buildings, this could be a way. You draw the same mesh multiple times, in a single command, with some differing parameters to reduce repetition (changing color, or even animation frames for meshes).
Unity introduced this in Unity 5.4 as a very welcome feature!
There’s a few samples out there for how to use Graphics.DrawMeshInstanced(), but I’d like to try and present a minimal code sample for what you need in case you want to start looking into Instanced Drawing.
Instanced Shader Properties
First, you’ll need a shader that’s set up to work with instancing.
This is achieved by adding a number of instancing specific properties to it, namely:
From the Unity Documentation on GPU Instancing:
Use this to instruct Unity to generate instancing variants. It is not necessary for surface Shaders.
Use this in the vertex Shader input/output structure to define an instance ID. See SV_InstanceID for more information.
Use this to make the instance ID accessible to Shader functions. It must be used at the very beginning of a vertex Shader, and is optional for fragment Shaders.
Every per-instance property must be defined in a specially named constant buffer. Use this pair of macros to wrap the properties you want to be made unique to each instance.
Use this to define a per-instance Shader property with a type and a name. In this example, the _Color property is unique.
Use this to access a per-instance Shader property declared in an instancing constant buffer. It uses an instance ID to index into the instance data array. The arrayName in the macro must match the one in UNITY_INSTANCING_BUFFER_END(name) macro.
Now that the minimum amount of properties needed to have a shader that is compatible with instancing have been identified, its time to put them to use in a shader!
Creating the shader
Here is a shader that uses the properties mentioned above. Its also available on GitHub under the MinimalInstancing project
_Color("Color", Color) = (1, 1, 1, 1)
#pragma vertex vert
#pragma fragment frag
float4 vertex : POSITION;
float4 vertex : SV_POSITION;
v2f vert(appdata v)
o.vertex = UnityObjectToClipPos(v.vertex);
fixed4 frag(v2f i) : SV_Target
float4 color = UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
Next, we’ll need a script that uses the shader combined with Graphics.DrawInstanced() to really show off just how much geometry you can draw with this API!
We need to use a material that uses the shader we created above, and we also need a model to draw, in this case a prefab I created with ProBuilder.
Draw Mesh Instanced gotchas
When it comes to actually invoking Graphics.DrawMeshInstanced() there are a couple things to point out:
1. An Array of Matrix4x4 are required. These matrices represent where the Meshes are to be drawn (translation, rotation & scale).
2. The Matrix4x4 array is limited to 1023 entries, so as the number of entities goes up, we’ll need to create more batches which could hinder performance, though 40,000 instances runs without a hitch.
3. The desired length of the Array is also required as a separate variable, but it does not need to match the 1023 limit as you can see in the code below.
public class DrawInstancedScript : MonoBehaviour
const float BATCH_MAX_FLOAT = 1023f;
const int BATCH_MAX = 1023;
public GameObject prefab;
public Material meshMaterial;
public int width;
public int depth;
public float spacing;
private MeshFilter mMeshFilter;
private MeshRenderer mMeshRenderer;
private Matrix4x4 matrices;
void Start ()
mMeshFilter = prefab.GetComponent<MeshFilter>();
mMeshRenderer = prefab.GetComponent<MeshRenderer>();
private void InitData()
int count = width * depth;
matrices = new Matrix4x4[count];
Vector3 pos = new Vector3();
Vector3 scale = new Vector3(1, 1, 1);
for (int i = 0; i < width; ++i)
for (int j = 0; j < depth; ++j)
int idx = i * depth + j;
matrices[idx] = Matrix4x4.identity;
pos.x = i * spacing;
pos.y = 0;
pos.z = j * spacing;
matrices[idx].SetTRS(pos, Quaternion.identity, scale);
void Update ()
int total = width * depth;
int batches = Mathf.CeilToInt(total / BATCH_MAX_FLOAT);
for (int i = 0; i < batches; ++i)
int batchCount = Mathf.Min(BATCH_MAX, total - (BATCH_MAX * i));
int start = Mathf.Max(0, (i - 1) * BATCH_MAX);
Matrix4x4 batchedMatrices = GetBatchedMatrices(start, batchCount);
Graphics.DrawMeshInstanced(mMeshFilter.sharedMesh, 0, meshMaterial, batchedMatrices, batchCount);
private Matrix4x4 GetBatchedMatrices(int offset, int batchCount)
Matrix4x4 batchedMatrices = new Matrix4x4[batchCount];
for(int i = 0; i < batchCount; ++i)
batchedMatrices[i] = matrices[i + offset];
DrawInstanced Script setup
Just one more step before we can see the result, which is assigning values to the DrawInstancedScript
For this demo, I went with a 200×200 sized grid for a total of 40,000 prisms being drawn on screen, with these settings:
With just enough spacing to be able to tell that the prisms are individual
Once we have this all set up, this is what we can see
This project (as well as the upcoming Part02) are available on GitHub:
In Part 2, I’ll show you how to use the Material Property Block combined with DrawMeshInstanced in order to create a scene with more variation, and here’s a sneak peak into it:
You can always follow me on Twitter @JavDev