Search code examples
c#arraysunity-game-enginegpuhlsl

How to index a ByteAddressBuffer in HLSL


I'm trying to access a ByteAddressBuffer in HLSL. The buffer contains vertices. I want to write a function that, given an index, will return the float3 at that index.

My current understanding is that the buffer stores a series of bytes which can be accessed in chunks of 4 bytes (and from testing I believe that accessing a non-aligned address rounds the index down to a multiple of 4).

Here is my function:

float3 load_vertex(int i) {
    int i_location = i * 12;
    float3 ret = float3(0.0f, 0.0f, 0.0f);
    ret.x = asfloat(vertices.Load(i_location    ));
    ret.y = asfloat(vertices.Load(i_location + 4));
    ret.z = asfloat(vertices.Load(i_location + 8));
    return ret;
}

I think that this should work: first the i_location shifts the index by 12 times the index (3 floats = 12 bytes) and then the values are accessed in steps of 4 (1 float = 4 bytes).

However, when I use this function and return the value, only the float3 returned at index 0 is correct. All other float3 values are erroneous.

Just in case I'm doing something mega-stupid, here's how I'm returning the values:

ByteAddressBuffer vertices;
ByteAddressBuffer indices;
RWStructuredBuffer<float3> result;
int index;

float3 load_vertex(int i) {...}

[numthreads(256,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    if (id.x == 0) {
        result[0] = load_vertex(index);
    }
}

If it's relevant, I'm using Unity and the buffer is being set and the shader dispatched like so:

mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
mesh.indexBufferTarget |= GraphicsBuffer.Target.Raw;
         
GraphicsBuffer vertexBuffer = mesh.GetVertexBuffer(0);
GraphicsBuffer indexBuffer = mesh.GetIndexBuffer();

closestPointShader.SetBuffer(0, "vertices", vertexBuffer);
closestPointShader.SetBuffer(0, "indices", indexBuffer);
closestPointShader.SetBuffer(0, "result", outBuffer);
closestPointShader.SetInt("index", indexValue);

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

Vector3[] k = new Vector3[1];
outBuffer.GetData(k);
Debug.Log("On GPU: " + k[0]);
Debug.Log("On CPU: " + mesh.vertices[indexValue]);

Thanks!


Solution

  • So as it turns out, the vertex buffer which unity provides has a stride of 56 bytes rather than 12 bytes. Altering the function to this:

    float3 load_vertex(int i) {
        int i_location = i * 56;
        float3 ret = float3(0.0f, 0.0f, 0.0f);
        ret.x = asfloat(vertices.Load(i_location    ));
        ret.y = asfloat(vertices.Load(i_location + 4));
        ret.z = asfloat(vertices.Load(i_location + 8));
        return ret;
    }
    

    Makes the function work fine. It was nice that this was pointed out in the documentation (/s)