Search code examples
iosswiftbuffermetalcompute-shader

Metal compute shader with 1D data buffer in and out?


I understand it is possible to pass a 1D array buffer to a metal shader, but is it possible to have it output to a 1D array buffer? I don't want it to write to a texture - I just need an array of processed values.

I can get values out with the shader with the following code, but they are one value at a time. Ideally I could get a whole array out (in the same order as the input 1D array buffer).

Any examples or pointers would be greatly appreciated!

var resultdata = [Float](repeating: 0, count: 3)
let outVectorBuffer = device.makeBuffer(bytes: &resultdata, length: MemoryLayout<float3>.size, options: [])
commandEncoder!.setBuffer(outVectorBuffer, offset: 0, index: 6)

commandBuffer!.addCompletedHandler {commandBuffer in
    let data = NSData(bytes: outVectorBuffer!.contents(), length: MemoryLayout<float3>.size)
    var out: float3 = float3(0,0,0)
    data.getBytes(&out, length: MemoryLayout<float3>.size)
    print("data: \(out)")
}

//In the Shader:
kernel void compute1d(
    ...
    device float3 &outBuffer [[buffer(6)]],
    outBuffer = float3(1.0, 2.0, 3.0);
 )

Solution

  • Two things:

    • You need to create the buffer large enough to hold however many float3 elements as you want. You really need to use .stride and not .size when calculating the buffer size, though. In particular, float3 has 16-byte alignment, so there's padding between elements in an array. So, you would use something like MemoryLayout<float3>.stride * desiredNumberOfElements.

    • Then, in the shader, you need to change the declaration of outBuffer from a reference to a pointer. So, device float3 *outBuffer [[buffer(6)]]. Then you can index into it to access the elements (e.g. outBuffer[2] = ...;).