Search Unity

Question Drawing a quad with DrawProceduralIndirectNow with a GraphicsBuffer

Discussion in 'Shaders' started by SneakyLemon, Jul 27, 2020.

  1. SneakyLemon

    SneakyLemon

    Joined:
    Aug 8, 2012
    Posts:
    5
    Hey all,

    First of all, I'm using Unity 2020.1. I've been bashing my head at this particular wall for a few days now, and finally found something working at https://github.com/cecarlsen/HowToDrawATriangle, which was a huge help. The problem is, whenever I try to launch the DrawProceduralIndirectNow + GraphicBuffer indices example it doesn't work. The one not using a GraphicsBuffer does. Is this a known bug, or is it borked in the current version of Unity?

    Here's my code that also doesn't work with a GraphicsBuffer, but works without it. Sorry for the curly braces, I come from Java ;)
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace DrawProceduralIndirect_Quad {
    4.     public class DrawQuad : MonoBehaviour {
    5.  
    6.         private static readonly int Vertices = Shader.PropertyToID("vertices");
    7.        
    8.         [SerializeField]
    9.         private ComputeShader computeShader;
    10.  
    11.         [SerializeField]
    12.         private Material material;
    13.  
    14.         private ComputeBuffer _vertexBuffer;
    15.         private GraphicsBuffer _graphicsBuffer;
    16.         private GraphicsBuffer _argsBuffer;
    17.  
    18.         private void OnEnable() {
    19.             _vertexBuffer = new ComputeBuffer(6, sizeof(float) * 4);
    20.             _graphicsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Index, 6, sizeof(int));
    21.  
    22.             var args = new[] {_vertexBuffer.count, 1, 0, 0};
    23.             _argsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, args.Length, sizeof(int));
    24.             _argsBuffer.SetData(args);
    25.  
    26.             computeShader.SetBuffer(0, "vertices", _vertexBuffer);
    27.             computeShader.SetBuffer(0, "indices", _graphicsBuffer);
    28.            
    29.             material.SetBuffer(Vertices, _vertexBuffer);
    30.         }
    31.  
    32.         private void OnDisable() {
    33.             _vertexBuffer.Release();
    34.             _graphicsBuffer.Release();
    35.             _argsBuffer.Release();
    36.         }
    37.  
    38.         private void Update() {
    39.             Graphics.ClearRandomWriteTargets();
    40.             Graphics.SetRandomWriteTarget(1, _vertexBuffer, false);
    41.             Graphics.SetRandomWriteTarget(1, _graphicsBuffer, false);
    42.  
    43.             computeShader.Dispatch(0, 1, 1, 1);
    44.  
    45.             var vertices = new Vector4[4];
    46.             var indices = new int[6];
    47.  
    48.             _vertexBuffer.GetData(vertices);
    49.             _graphicsBuffer.GetData(indices);
    50.  
    51.             // this logs correct values for both vertices and indices
    52.             Debug.Log($"vertices: {string.Join(", ", vertices)}");
    53.             Debug.Log($"indices: {string.Join(", ", indices)}");
    54.         }
    55.        
    56.         private void OnRenderObject() {
    57.             material.SetPass(0);
    58.             Graphics.DrawProceduralIndirectNow(MeshTopology.Triangles, _graphicsBuffer, _argsBuffer); // doesn't work
    59.             // Graphics.DrawProceduralIndirectNow(MeshTopology.Triangles, _argsBuffer); -- this works fine, but draws a triangle because it lacks indices
    60.         }
    61.     }
    62. }
    63.  
    Compute shader:
    Code (CSharp):
    1. #pragma kernel CSMain
    2.  
    3. RWStructuredBuffer<float4> vertices;
    4. RWStructuredBuffer<int> indices;
    5.  
    6. [numthreads(1,1,1)]
    7. void CSMain (uint3 id : SV_DispatchThreadID) {
    8.     vertices[0] = float4(0, 0, 0, 1);
    9.     vertices[1] = float4(0, 1, 0, 1);
    10.     vertices[2] = float4(1, 1, 0, 1);
    11.     vertices[3] = float4(1, 0, 0, 1);
    12.  
    13.     indices[0] = 0;
    14.     indices[1] = 1;
    15.     indices[2] = 2;
    16.     indices[3] = 0;
    17.     indices[4] = 2;
    18.     indices[5] = 3;
    19. }
    20.  
    Vertex shader:
    Code (CSharp):
    1. Shader "Unlit/QuadShader"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "RenderType"="Opaque" }
    10.         LOD 100
    11.  
    12.         Pass
    13.         {
    14.             CGPROGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             #pragma target 5.0
    18.  
    19.             #include "UnityCG.cginc"
    20.  
    21.             struct v2f
    22.             {
    23.                 float4 vertex : SV_POSITION;
    24.             };
    25.  
    26.             StructuredBuffer<float4> vertices;
    27.  
    28.             sampler2D _MainTex;
    29.             float4 _MainTex_ST;
    30.  
    31.             v2f vert (uint id : SV_VertexID) {
    32.                 v2f o;
    33.                 o.vertex = UnityObjectToClipPos(vertices[id]);
    34.                 return o;
    35.             }
    36.  
    37.             fixed4 frag (v2f i) : SV_Target {
    38.                 return float4(1, 1, 1, 1);
    39.             }
    40.             ENDCG
    41.         }
    42.     }
    43. }
    44.  
    I would appreciate any help on this :)
     
  2. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    The GraphicsBuffer is not writable by the compute shader by default.
     
  3. SneakyLemon

    SneakyLemon

    Joined:
    Aug 8, 2012
    Posts:
    5
  4. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    Did you try looking behind the triangle? Maybe it's just being backface culled due to the winding order. You could also use Renderdoc to verify.
     
  5. SneakyLemon

    SneakyLemon

    Joined:
    Aug 8, 2012
    Posts:
    5
    I have, and there's nothing there. The winding order must be correct, because it works fine without using the graphics buffer. I've done enough procedural mesh generation lately to be sure, so that's unfortunately not it. I haven't heard of Renderdoc though, and it look pretty interesting, so thanks! I'll give it a shot.
     
  6. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    When doing procedural and compute shaders, Renderdoc is a must to see what's really going on.
     
  7. SneakyLemon

    SneakyLemon

    Joined:
    Aug 8, 2012
    Posts:
    5
    I have generated a renderdoc snapshot using Unity's built-in integration, but to be honest I have no idea what I'm looking at. I saw a quad being generated at the correct position, so I changed my code to a pentagon instead, and I can't find that shape anywhere in renderdoc. I've attached the file, perhaps you could take a look in your spare time? I'll keep looking into this, but I'm going to have to learn how renderdoc works first.

    I also switched to using a RWByteAddressBuffer:
    Code (CSharp):
    1.  
    2. #pragma kernel CSMain
    3. #pragma enable_d3d11_debug_symbols
    4.  
    5. RWStructuredBuffer<float4> vertices;
    6. RWByteAddressBuffer indices;
    7.  
    8. [numthreads(1,1,1)]
    9. void CSMain (uint3 id : SV_DispatchThreadID) {
    10.     vertices[0] = float4(0, 0, 0, 1);
    11.     vertices[1] = float4(0, 1, 0, 1);
    12.     vertices[2] = float4(1, 1.5, 0, 1);
    13.     vertices[3] = float4(1, 1, 0, 1);
    14.     vertices[4] = float4(1, 0, 0, 1);
    15.    
    16.     indices.Store(0 * 4, 0);
    17.     indices.Store(1 * 4, 1);
    18.     indices.Store(2 * 4, 2);
    19.     indices.Store(3 * 4, 0);
    20.     indices.Store(4 * 4, 2);
    21.     indices.Store(5 * 4, 3);
    22.     indices.Store(6 * 4, 0);
    23.     indices.Store(7 * 4, 3);
    24.     indices.Store(8 * 4, 4);
    25. }
    26.  
    And changed the graphics buffer to

    _graphicsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Index | GraphicsBuffer.Target.Raw, 9, sizeof(int));
     

    Attached Files:

  8. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    Reviewing your code, I found a problem. The args buffer needs to contain 5 elements, not 4. Also, the first element should be the number of indices drawn, not the number of vertices.

    These are the contents of your index buffer, which seem correct:
    upload_2020-7-28_11-30-23.png

    But your args buffer is telling the draw call to use 5 indices, which is not a valid number (it needs to be a multiple of 3 for triangle topology).

    upload_2020-7-28_11-31-52.png
    (The missing 5th element might also cause undefined behavior)


    The Mesh Viewer tab shows nonsense because of this:
    upload_2020-7-28_11-34-9.png
     
    cecarlsen likes this.
  9. SneakyLemon

    SneakyLemon

    Joined:
    Aug 8, 2012
    Posts:
    5
    So, I fixed all of the above, and still nothing is being rendered.

    upload_2020-7-29_16-53-24.png

    I changed my args buffer to:
    Code (CSharp):
    1.  
    2. var args = new[] {9, 1, 0, 0, 0};
    3. _argsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, args.Length, sizeof(int));
    4. _argsBuffer.SetData(args);
    5.  
    upload_2020-7-29_16-53-58.png

    The mesh viewer tab still shows nonsense:
    upload_2020-7-29_16-54-49.png
     
  10. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    864
    Holy cow! I finally got DrawProceduralIndirect to work with GraphicsBuffer.Target.Index. In this special case, Unity's documentation is flawed: the draw args buffer need to be length 5 not 4, and the first index represents index count, not vertex count. Thanks for the tip!

    Still not getting any shadows though. And I can't get it to work on Oculus Quest.
     
    Last edited: Mar 10, 2022