Compute shader ComputeBuffer.GetData returns random values when using InterlockedAdd

Hello !
I’m writing a compute shader where I need to use InterlockedAdd to atomically increment a counter.

After dispatching the shader, I use GetData to retrieve the value of the counter but the result is completely random.
What’s really strange is that if I move GetData to the beginning of the next frame, the result is correct, so it seems InterlockedAdd is working properly, but using GetData immediately after dispatching the shader somehow doesn’t return the final value.

Here’s a very simple example :

#pragma kernel TestShader

RWStructuredBuffer<uint> buffer;

[numthreads(64,1,1)]
void TestShader(uint3 id : SV_DispatchThreadID) {
    uint _;
    InterlockedAdd(buffer[0], 1, _);
}
using UnityEngine;

public class TestShader : MonoBehaviour {
    public ComputeShader shader;
    private ComputeBuffer buffer;

    void Start() {
        buffer = new ComputeBuffer(1, sizeof(uint));
        shader.SetBuffer(0, "buffer", buffer);
    }

    void Update() {
        // Here it works
        //uint[] prevData = new uint[1];
        //buffer.GetData(prevData);
        //Debug.Log(prevData[0]); // 16384

        uint[] data = new uint[1];
        buffer.SetData(data);
        shader.Dispatch(0, 256, 1, 1);

        buffer.GetData(data);
        Debug.Log(data[0]); // Random value :(
    }

    void OnApplicationQuit() {
        buffer.Dispose();
    }
}

Am I missing something here ? Or is this a bug ?

I’ve just tried the same on Windows and it always returns the correct result (I was previously using Ubuntu) :thinking:
So maybe it’s related to the OS ?

I’ve also tried on Unity 6.2, it’s the same :cry:

The reason is that you are not waiting for it to complete so its reading while running.

There are few different version of callback (check docs for different versions). This one worked for me. In your case make a NativeArray<uint> or (untested) NativeReference<uint>.

The “Done” is in the callback (req)=>{DoSomethingWhenDone;} after WaitForCompletion()

Bear in mind there is latency for readback so you dont want block the main thread doing nothing for a whole frame, hence why it comes through on the next frame for you.

            AsyncGPUReadbackRequest req;

            // async readback + callback executed on completion
            req = AsyncGPUReadback.RequestIntoNativeArray(ref dataOut, computeBuffer, (req) =>
            {
                Debug.Log("Done");
            });

            // Can only check if req.done once as main thread must be free for callback, otherwise block and WaitForCompletion
            if (!req.done) req.WaitForCompletion();

Thank you very much !
Calling AsyncGPUReadback, then immediately WaitForCompletion seems to work ! :slight_smile:

I know it blocks the main thread, but my goal is to use the result of the compute shader for a draw call, so unfortunately I have no choice but to wait for it to finish. Though I guess I could start it at the begining of the frame and wait at the end.

It’s interesting that GetData doesn’t behave the same :thinking: According to the documentation, if any GPU work has been submitted that writes to this buffer, Unity waits for the tasks to complete before it retrieves the requested data, guaranteeing this function returns the most up to date results.
Clearly that means it shouldn’t read while the shader is still running, right ?

I found another solution that seems faster :slight_smile:
I’m now using a counter buffer. I increment the counter with IncrementCounter in the shader, then I get the value with CopyCount and GetData in C#.
I’m still not sure why GetData alone doesn’t work, or why adding CopyCount makes it work, I still believe there might be a bug somewhere, but at least there’s a workaround :sweat_smile:

Here’s the final code :

#pragma kernel TestShader

RWStructuredBuffer<uint> buffer;

[numthreads(64,1,1)]
void TestShader(uint3 id : SV_DispatchThreadID) {
    buffer.IncrementCounter();
}
using UnityEngine;

public class TestShader : MonoBehaviour {
    public ComputeShader shader;
    private ComputeBuffer buffer;

    void Start() {
        buffer = new ComputeBuffer(1, sizeof(uint), ComputeBufferType.Counter);
        shader.SetBuffer(0, "buffer", buffer);
    }

    void Update() {
        buffer.SetCounterValue(0);
        shader.Dispatch(0, 256, 1, 1);

        ComputeBuffer.CopyCount(buffer, buffer, 0);
        uint[] data = new uint[1];
        buffer.GetData(data);
        Debug.Log(data[0]); // 16384
    }

    void OnApplicationQuit() {
        buffer.Dispose();
    }
}

Not sure what exactly you mean by “use the result of for a draw call” but if you want to draw a number if instances of one object you can use indirect rendering, where you provide a GPU buffer which contains the number of elements you want to draw (“Graphics.RenderMeshIndirect”). That way you dont need to do a read back.

Not sure if that what you want to do though. But in must cases you probably would not need a readback.

Yes, I use indirect rendering but the counter is actually the number of commands, not the number of instances (each of these commands draw a different “mesh” with a different number of vertices).
So the number of vertices of each command doesn’t need to be read back, but unfortunately the command count must be passed as an argument to Graphics.RenderPrimitivesIndexedIndirect, I didn’t find any way to provide it as a buffer.