Instancing questions

Hi
Very confused about instancing trying to make a lot of grass for a game thought id try this approach but …
First the meshes only exist for 1 frame? so they have to be constantly called in update is this right or is there some other command than the one im using in the code below that has the mesh continue to exist ?This code slows my fps down to 5 without me looking at the generated meshes.

using UnityEngine;
using System.Collections;

public class drawm : MonoBehaviour {
    public Mesh mesh;
    public Material material;
    int a=0;

    public void Update() {
        for (int a = 0; a < 100000; a++) {
            int b = a;
            int c=a;
            transform.position = new Vector3 (b, 0, c);
            Graphics.DrawMesh (mesh, transform.position, Quaternion.identity, material, 0);
        }
          
    }


}

I looked on github and found this one project that looks promising it is able to run at 60 fps with 100k meshes and 1 million is need to slow it down to the abovementioned 5 fps.But not too clued in on coding so could someone tell me what about the code helps it do that and can that code be integrated into my above sample?
Couple issues with this code below.
First it only works with unity meshes if i try put in any other mesh its invisible.
Second it only works with its shader if i put in any other shader it doesn’t work.
Third the shader isn’t a cutout is it possible to convert it to a cutout shade and will still work with the code?
And lastly and this totally confuses me the instanceCount amount it used to start with was 100k i tried setting the instancecount to 1000 at initiation but that didn’t work so i put the line in the Start which does restrict it but wjhat i don’t see is where is instructs the instancecount to be 100k between the initiation line and the start one?

// https://docs.unity3d.com/560/Documentation/ScriptReference/Graphics.DrawMeshInstancedIndirect.html

using UnityEngine;
public class InstancedIndirectExample : MonoBehaviour
{
    public int instanceCount = 1000;
    public Mesh instanceMesh;
    public Material instanceMaterial;
    private int cachedInstanceCount = -1;
    private ComputeBuffer positionBuffer;
    private ComputeBuffer argsBuffer;
    private ComputeBuffer colorBuffer;

    private uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
    void Start()
    {
        argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
        UpdateBuffers();
        instanceCount = 1000;?????????

    }

    void Update()
    {
        // Update starting position buffer
        if (cachedInstanceCount != instanceCount) UpdateBuffers();

        // Pad input
        if (Input.GetAxisRaw("Horizontal") != 0.0f) instanceCount = (int)Mathf.Clamp(instanceCount + Input.GetAxis("Horizontal") * 40000, 1.0f, 5000000.0f);
        // Render
      //  instanceMaterial.SetBuffer("positionBuffer", positionBuffer);
        Graphics.DrawMeshInstancedIndirect(instanceMesh, 0, instanceMaterial, new Bounds(Vector3.zero, new Vector3(100.0f, 100.0f, 100.0f)), argsBuffer);
    }
    void OnGUI()
    {
        GUI.Label(new Rect(265, 12, 200, 30), "Instance Count: " + instanceCount.ToString("N0"));
        instanceCount = (int)GUI.HorizontalSlider(new Rect(25, 20, 200, 30), (float)instanceCount, 1.0f, 5000000.0f);
    }
    void UpdateBuffers()
    {
        if ( instanceCount < 1 ) instanceCount = 1;

        // Positions & Colors
        if (positionBuffer != null) positionBuffer.Release();
        if (colorBuffer != null) colorBuffer.Release();

        positionBuffer    = new ComputeBuffer(instanceCount, 16);
        colorBuffer        = new ComputeBuffer(instanceCount, 4*4);

        Vector4[] positions = new Vector4[instanceCount];
        Vector4[] colors    = new Vector4[instanceCount];

        for (int i=0; i < instanceCount; i++)
        {
            float angle = Random.Range(0.0f, Mathf.PI * 2.0f);
            float distance = Random.Range(20.0f, 100.0f);
            float height = Random.Range(-2.0f, 2.0f);
            float size = Random.Range(0.05f, 0.25f);
            positions[i]    = new Vector4(Mathf.Sin(angle) * distance, height, Mathf.Cos(angle) * distance, size);
            colors[i]        = new Vector4( Random.value, Random.value, Random.value, 1f );
        }

        positionBuffer.SetData(positions);
        colorBuffer.SetData(colors);

        instanceMaterial.SetBuffer("positionBuffer", positionBuffer);
        instanceMaterial.SetBuffer("colorBuffer", colorBuffer);

        // indirect args
        uint numIndices = (instanceMesh != null) ? (uint)instanceMesh.GetIndexCount(0) : 0;
        args[0] = numIndices;
        args[1] = (uint)instanceCount;
        argsBuffer.SetData(args);
        cachedInstanceCount = instanceCount;
    }

    void OnDisable()
    {
        if (positionBuffer != null) positionBuffer.Release();
        positionBuffer = null;

        if (colorBuffer != null) colorBuffer.Release();
        colorBuffer = null;

        if (argsBuffer != null) argsBuffer.Release();
        argsBuffer = null;
    }
}

And the shader code

Shader "Instanced/InstancedIndirectSurfaceShader"
{
    Properties{
        _MainTex("Albedo (RGB)", 2D) = "white" {}
    _Glossiness("Smoothness", Range(0,1)) = 0.5
        _Metallic("Metallic", Range(0,1)) = 0.0
    }
        SubShader{
        Tags{ "RenderType" = "Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model
#pragma surface surf Standard addshadow
#pragma multi_compile_instancing
#pragma instancing_options procedural:setup

        sampler2D _MainTex;

    struct Input {
        float2 uv_MainTex;
    };

#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    StructuredBuffer<float4> positionBuffer;
    StructuredBuffer<float4> colorBuffer;
#endif


    void rotate2D(inout float2 v, float r)
    {
        float s, c;
        sincos(r, s, c);
        v = float2(v.x * c - v.y * s, v.x * s + v.y * c);
    }

    void setup()
    {
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
        float4 data = positionBuffer[unity_InstanceID];

        float rotation = data.w * data.w * _Time.y * 0.5f;
        rotate2D(data.xz, rotation);

        unity_ObjectToWorld._11_21_31_41 = float4(data.w, 0, 0, 0);
        unity_ObjectToWorld._12_22_32_42 = float4(0, data.w, 0, 0);
        unity_ObjectToWorld._13_23_33_43 = float4(0, 0, data.w, 0);
        unity_ObjectToWorld._14_24_34_44 = float4(data.xyz, 1);
        unity_WorldToObject = unity_ObjectToWorld;
        unity_WorldToObject._14_24_34 *= -1;
        unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
#endif
    }

    half _Glossiness;
    half _Metallic;

    void surf(Input IN, inout SurfaceOutputStandard o)
    {
        float4 col = 1.0f;

#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
        //col.gb = (float)(unity_InstanceID % 256) / 255.0f;
        col = colorBuffer[unity_InstanceID];
#else
        //col.gb = float4(0, 0, 1, 1);
        col = float4(0, 0, 1, 1);
#endif



        fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * col;
        o.Albedo = c.rgb;
        o.Metallic = _Metallic;
        o.Smoothness = _Glossiness;
        o.Alpha = c.a;
    }
    ENDCG
    }
        FallBack "Diffuse"
}

I tried using this shader with my example but the fps remained at 5.
I know its a lot to ask but if someone is feeling helpful if you could explain a couple of these thing to me in layman terms if possible.
Thx in advance

Hi.

There is 2 ways to do instancing with the Unity API.

Graphics.DrawMeshInstanced and Graphics.DrawMeshInstancedIndirect

Common for both of these is that you need to call the functions every frame. The meshes are not saved between frames. This is good since you probably want to change the selection of meshes to render based on the position/orientation of the camers.

In your first example you call Graphics.DrawMesh where you need to send each mesh to rendering manually. This is slow.

A first step would be to use DrawMeshInstanced.

With this API you can render up to 1023 meshes for each call. You can send a list or array with Matrix4x4 structs that holds the position, scale and rotation for each of these meshes. You make them with Matrix4x4.TRS function.

Another option if you have a lot of meshes is to use drawmeshinstancedIndirect. This has no 1023 limit per call but requires a lot more setup and a custom shader.

What you do here is make a compute buffer that holds the mesh position info. and you call a Setup function in the shader where you set the ObjectToWorld and WorldToObject matrix for the instance.
This is what your 2nd example does.

Hi Lennart thx for the info think i will try the DrawmeshInstanced option first.
Don’t know anything about the Matrix4x4.TRS u mentioned will read up on it.
Think maybe layman is a bit to advanced for me should have asked to explain to me as if im a toddler:).
Any clue why the drawmeshinstancedIndirect only works with unity’s meshes cube,capsule etc?