Drawing a quad with DrawProceduralIndirectNow with a GraphicsBuffer

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 GitHub - cecarlsen/HowToDrawATriangle: Examples of how to draw a procedural triangle in Unity3D, 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 :wink:

using UnityEngine;

namespace DrawProceduralIndirect_Quad {
    public class DrawQuad : MonoBehaviour {

        private static readonly int Vertices = Shader.PropertyToID("vertices");
       
        [SerializeField]
        private ComputeShader computeShader;

        [SerializeField]
        private Material material;

        private ComputeBuffer _vertexBuffer;
        private GraphicsBuffer _graphicsBuffer;
        private GraphicsBuffer _argsBuffer;

        private void OnEnable() {
            _vertexBuffer = new ComputeBuffer(6, sizeof(float) * 4);
            _graphicsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Index, 6, sizeof(int));

            var args = new[] {_vertexBuffer.count, 1, 0, 0};
            _argsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, args.Length, sizeof(int));
            _argsBuffer.SetData(args);

            computeShader.SetBuffer(0, "vertices", _vertexBuffer);
            computeShader.SetBuffer(0, "indices", _graphicsBuffer);
           
            material.SetBuffer(Vertices, _vertexBuffer);
        }

        private void OnDisable() {
            _vertexBuffer.Release();
            _graphicsBuffer.Release();
            _argsBuffer.Release();
        }

        private void Update() {
            Graphics.ClearRandomWriteTargets();
            Graphics.SetRandomWriteTarget(1, _vertexBuffer, false);
            Graphics.SetRandomWriteTarget(1, _graphicsBuffer, false);

            computeShader.Dispatch(0, 1, 1, 1);

            var vertices = new Vector4[4];
            var indices = new int[6];

            _vertexBuffer.GetData(vertices);
            _graphicsBuffer.GetData(indices);

            // this logs correct values for both vertices and indices
            Debug.Log($"vertices: {string.Join(", ", vertices)}");
            Debug.Log($"indices: {string.Join(", ", indices)}");
        }
       
        private void OnRenderObject() {
            material.SetPass(0);
            Graphics.DrawProceduralIndirectNow(MeshTopology.Triangles, _graphicsBuffer, _argsBuffer); // doesn't work
            // Graphics.DrawProceduralIndirectNow(MeshTopology.Triangles, _argsBuffer); -- this works fine, but draws a triangle because it lacks indices
        }
    }
}

Compute shader:

#pragma kernel CSMain

RWStructuredBuffer<float4> vertices;
RWStructuredBuffer<int> indices;

[numthreads(1,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID) {
    vertices[0] = float4(0, 0, 0, 1);
    vertices[1] = float4(0, 1, 0, 1);
    vertices[2] = float4(1, 1, 0, 1);
    vertices[3] = float4(1, 0, 0, 1);

    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 2;
    indices[3] = 0;
    indices[4] = 2;
    indices[5] = 3;
}

Vertex shader:

Shader "Unlit/QuadShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 5.0

            #include "UnityCG.cginc"

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            StructuredBuffer<float4> vertices;

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (uint id : SV_VertexID) {
                v2f o;
                o.vertex = UnityObjectToClipPos(vertices[id]);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                return float4(1, 1, 1, 1);
            }
            ENDCG
        }
    }
}

I would appreciate any help on this :slight_smile:

The GraphicsBuffer is not writable by the compute shader by default.

It is possible in 2020.1, as evidenced by this answer: GraphicsBuffer, Mesh vertices and Compute shaders . Also, I am retrieving the index data from the graphics buffer and the values are correct.

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.

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.

When doing procedural and compute shaders, Renderdoc is a must to see what’s really going on.

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:

#pragma kernel CSMain
#pragma enable_d3d11_debug_symbols

RWStructuredBuffer<float4> vertices;
RWByteAddressBuffer indices;

[numthreads(1,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID) {
    vertices[0] = float4(0, 0, 0, 1);
    vertices[1] = float4(0, 1, 0, 1);
    vertices[2] = float4(1, 1.5, 0, 1);
    vertices[3] = float4(1, 1, 0, 1);
    vertices[4] = float4(1, 0, 0, 1);
   
    indices.Store(0 * 4, 0);
    indices.Store(1 * 4, 1);
    indices.Store(2 * 4, 2);
    indices.Store(3 * 4, 0);
    indices.Store(4 * 4, 2);
    indices.Store(5 * 4, 3);
    indices.Store(6 * 4, 0);
    indices.Store(7 * 4, 3);
    indices.Store(8 * 4, 4);
}

And changed the graphics buffer to ```
_graphicsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Index | GraphicsBuffer.Target.Raw, 9, sizeof(int));


[6141426--670194--DrawPentagon.zip|attachment](upload://3AkHrsKUYeKNAbmukuQtzyzzcmL.zip) (254 KB)

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:

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).


(The missing 5th element might also cause undefined behavior)

The Mesh Viewer tab shows nonsense because of this:

1 Like

So, I fixed all of the above, and still nothing is being rendered.

I changed my args buffer to:

var args = new[] {9, 1, 0, 0, 0};
_argsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, args.Length, sizeof(int));
_argsBuffer.SetData(args);

The mesh viewer tab still shows nonsense:

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.