Search Unity

Question Nothing appearing while using DrawMeshInstancedIndirect, DrawMeshInstanced only partially working

Discussion in 'General Graphics' started by EmmetOT, Jul 19, 2021.

  1. EmmetOT

    EmmetOT

    Joined:
    Apr 25, 2016
    Posts:
    44
    I'm trying to draw stuff using DrawMeshInstancedIndirect, but absolutely nothing is appearing. I understand that DrawMeshInstanced supports up to 1023 instances, but when I use it it only draws up to a max of half of that.

    upload_2021-7-19_23-12-22.png
    Successfully drawing 400 red quads using DrawMeshInstanced. Virtually the same code will not draw anything using DrawMeshInstancedIndirect.

    Things I've tried:
    • I've gone through all the docs and tutorials I could find regarding this.
    • "Enable GPU Instancing" is ticked on the material.
    • This occurs using the built-in renderer in a new project (2020.3.0f1) with almost zero packages imported.
    • I've double checked that the information in the buffers is correct, and the fact that DrawMeshInstanced works (kinda) implies that it is, too.
    • It doesn't seem to make a difference whether I use a MaterialPropertyBlock or set the data right on the material. Likewise instantiating the material makes no difference.
    I'll attach a minimal project here, but this is the relevant bit of C# (the bit that populates the buffer/shader data, and the update loop):

    Code (CSharp):
    1.  
    2.     private void Init()
    3.     {
    4.         if (m_materialInstance)
    5.             DestroyImmediate(m_materialInstance);
    6.  
    7.         m_materialInstance = new Material(m_material);
    8.  
    9.         m_indirectArgsBuffer?.Dispose();
    10.         m_positionsBuffer?.Dispose();
    11.  
    12.         m_indirectArgsBuffer = new ComputeBuffer(5, sizeof(uint));
    13.         m_positionsBuffer = new ComputeBuffer(m_count, sizeof(float) * 2);
    14.  
    15.         // set up the indirect args: [6, number of instances, 0, 0, 0]
    16.         m_indirectArgs[0] = m_mesh.GetIndexCount(0);
    17.         m_indirectArgs[1] = (uint)m_count;
    18.         m_indirectArgs[2] = m_mesh.GetIndexStart(0);
    19.         m_indirectArgs[3] = m_mesh.GetBaseVertex(0);
    20.         m_indirectArgsBuffer.SetData(m_indirectArgs);
    21.    
    22.         // generate a grid of positions and send them to the buffer
    23.         GenerateGridPositions();
    24.         m_positionsBuffer.SetData(m_meshPositions);
    25.    
    26.         m_propertyBlock = new MaterialPropertyBlock();
    27.         m_propertyBlock.SetBuffer(MATERIAL_BUFFER_PROPERTY, m_positionsBuffer);
    28.         m_propertyBlock.SetFloat(SIZE_PROPERTY, m_size);
    29.         m_propertyBlock.SetColor(COLOUR_PROPERTY, m_colour);
    30.     }
    31.  
    32.     private void Update()
    33.     {
    34.         if (!m_materialInstance || !m_mesh)
    35.             return;
    36.    
    37.         if (m_count != m_matrices.Length)
    38.             Init();
    39.    
    40.         if (m_drawDirect)
    41.         {
    42.             Graphics.DrawMeshInstanced(m_mesh, 0, m_materialInstance, m_matrices, m_count, m_propertyBlock);
    43.         }
    44.  
    45.         if (m_drawIndirect)
    46.         {
    47.             // indirect args buffer = [6, number of instances, 0, 0, 0]
    48.             Graphics.DrawMeshInstancedIndirect(m_mesh, 0, m_materialInstance, m_bounds, m_indirectArgsBuffer, properties: m_propertyBlock, layer: gameObject.layer);
    49.         }
    50.     }
    51.    
    and the shader itself:


    Code (CSharp):
    1. Shader "Unlit/InstancedTestShader"
    2. {
    3.     SubShader
    4.     {
    5.         Pass
    6.         {
    7.             CGPROGRAM
    8.             #pragma vertex vert
    9.             #pragma fragment frag
    10.             #pragma target 4.5
    11.             #pragma multi_compile_instancing
    12.  
    13.             #include "UnityCG.cginc"
    14.  
    15.             float4 _Colour;
    16.             float _Size;
    17.             StructuredBuffer<float2> _PositionsBuffer;
    18.  
    19.             struct appdata
    20.             {
    21.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    22.                 float4 vertex : POSITION;
    23.             };
    24.  
    25.             struct v2f
    26.             {
    27.                 float4 vertex : SV_POSITION;
    28.             };
    29.  
    30.             v2f vert(appdata v, uint instanceID : SV_InstanceID)
    31.             {
    32.                 v2f o;
    33.                 UNITY_SETUP_INSTANCE_ID(v);
    34.  
    35.                 float2 position = _PositionsBuffer[instanceID];
    36.                 o.vertex = UnityObjectToClipPos(v.vertex * _Size + float3(position, 0.));
    37.  
    38.                 return o;
    39.             }
    40.  
    41.             fixed4 frag(v2f i) : SV_Target
    42.             {
    43.                 return _Colour;
    44.             }
    45.  
    46.             ENDCG
    47.         }
    48.     }
    49. }
    50.  
    Please help me figure this out, it's driving me crazy! Again, the minimal reproduction project is also attached.
     

    Attached Files:

    Last edited: Jul 19, 2021
  2. blindgr

    blindgr

    Joined:
    Jul 17, 2021
    Posts:
    4
    The matrix is missing, add a buffer to store matrices or rebuild it in shader.
    For your example you can simply change the vertex out as

    o.vertex = mul(UNITY_MATRIX_VP, float4(v.vertex * _Size + float3(position, 0.), 1.0));
     
  3. EmmetOT

    EmmetOT

    Joined:
    Apr 25, 2016
    Posts:
    44
    Hm, that doesn't seem to work. Changing the line in the vertex shader just makes it so nothing renders at all.

    Isn't the matrix multiplication already covered by UnityObjectToClipPos?
     
  4. blindgr

    blindgr

    Joined:
    Jul 17, 2021
    Posts:
    4
    Sorry, I'm missing this line.

    m_indirectArgsBuffer = new ComputeBuffer(5, sizeof(uint), ComputeBufferType.IndirectArguments);

    The CommandBufferType should be specified.

    I dont know how unity implement the object to world matrix in DrawMeshInstancedIndirect, but usually the matrix should be passed by cbuffer.
     
    Last edited: Jul 20, 2021
  5. EmmetOT

    EmmetOT

    Joined:
    Apr 25, 2016
    Posts:
    44
    Oh yeah, I had that before, must have removed it accidentally before posting. This, as well as the fact that I somehow had the 'INSTANCING_ON' keyword set on my material (only accessible in the debug view!) were the issues. Changing them both got Indirect working!