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