How to index a ByteAddressBuffer in HLSL

Hi Unity forums!

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, 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]);

Thank you!

So as it turns out, the stride of each element in the vertex buffer is 56 rather than 12. Replacing line 2 of the function with

int i_location = i * 56;

Makes it work perfectly.

If anyone could enlighten me as to what is being stored in the other 44 bytes that would be great because I have no idea.

Vertex buffer can contain a lot more than positions like normals, uvs, etc. Check here : Unity - Scripting API: Mesh.GetVertexAttributes

1 Like

hi, how do you read the buffer of “indices”? use chunks of 4 bytes?
i tried like this but the result not right:

float3 getVertex(uint id){
    uint vId = asuint( _IndexBuffer.Load(id*4));
    vId*=56;//the vertex buffer which unity provides has a stride of 56 bytes rather than 12 bytes
    float3 ret = 0;
    ret.x = asfloat(_VertexBuffer.Load(vId));
    ret.y = asfloat(_VertexBuffer.Load(vId+4));
    ret.z = asfloat(_VertexBuffer.Load(vId+8));
    return ret;
}

[numthreads(512,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    if(id.x>_TriCount)return;

    uint pId = id.x*3;

    float4 a = mul(_TargetObjectToWorld, float4(getVertex(pId),1));
    float4 b = mul(_TargetObjectToWorld, float4(getVertex(pId+1),1));
    float4 c = mul(_TargetObjectToWorld, float4(getVertex(pId+2),1));
...