creating Argument Buffers

In the keynote presentation discussing the new metal 2 features it was mentioned that you would create an argument buffer from a MTLFunction. When I try that I always end up with a BAD ACCESS error on runtime. What I try to do is:

- define a struct to be buffered in the metal shader

- set the struct to be buffered as a parameter of the kernel function.

- after creating the function from the library I call makeIndirectArgumentEncoder on that kernel function.


Trying to set a buffer, texture orconstant data is then going to fail. What am I missing?


This is what happens on the CPU side:

let library = device.makeDefaultLibrary()!
let function = library.makeFunction(name: "trace")!
let argumentEncoder = function.makeIndirectArgumentEncoder(bufferIndex: 0)
     
argumentEncoder.setTexture(argumentInImage, index:0) //BAD ACCESS ERROR right here.
argumentEncoder.setBuffer(worldBuffer, offset: 0, index: 1)


And in the metal shader:

struct CameraState {
    texture2d<float,access::read_write>  in [[id(0)]];
    device Sphere *spheres [[id(1)]];
    ...
};
kernel void trace(
                  constant CameraState &cameraState [[ buffer(0) ]],
    ...
){...}


When I look into the assembler code that crashed I can see the comment: ' "0 && "could't found the resource in the Layout Array\n"" '.


As far as I understand makeIndirectArgumentEncoder tries to optimally create the buffer for me that is then used as input at index 0 when I call the function. It seems though that the while making the argument encoder there could not be found a match for the texture buffer in the struct.


Actually I'm not quite sure if the [[id(x)]] declaration in the struct is correct, but that was the only hint I found in the demo code, but it does not work with nor without this.


Maybe knows what I'm doing wrong here. I would be grateful!


Cheers,

Enie

Accepted Reply

You need to add the a call to MTLIndirectArgumentEncoder.setIndirectArgumentBuffer before you can encode other resources such as the texture. So change your swift code as follows:


let library = device.makeDefaultLibrary()!
let function = library.makeFunction(name: "trace")!

argumentEncoder.setIndirectArgumentBuffer(cameraStateArgumentBuffer)
argumentEncoder.setTexture(argumentInImage, index:0) //<- Encode 'argumentInImage' to 'cameraStateArgumentBuffer'
argumentEncoder.setBuffer(worldBuffer, offset: 0, index:0)


And, if you haven't arlready, you need to set 'commandStateArgumentBuffer' before you run your kernel as follows


computeCommandEncoder.setBuffer(cameraStateArgumentBuffer, offset:0, index:0);


Note: As of Seed 2 the class and methods names have removed the word 'Indirect'. So, for example, instead of 'setIdirectArgumentBuffer' it's now just 'setArgumentBuffer'.

Replies

You need to add the a call to MTLIndirectArgumentEncoder.setIndirectArgumentBuffer before you can encode other resources such as the texture. So change your swift code as follows:


let library = device.makeDefaultLibrary()!
let function = library.makeFunction(name: "trace")!

argumentEncoder.setIndirectArgumentBuffer(cameraStateArgumentBuffer)
argumentEncoder.setTexture(argumentInImage, index:0) //<- Encode 'argumentInImage' to 'cameraStateArgumentBuffer'
argumentEncoder.setBuffer(worldBuffer, offset: 0, index:0)


And, if you haven't arlready, you need to set 'commandStateArgumentBuffer' before you run your kernel as follows


computeCommandEncoder.setBuffer(cameraStateArgumentBuffer, offset:0, index:0);


Note: As of Seed 2 the class and methods names have removed the word 'Indirect'. So, for example, instead of 'setIdirectArgumentBuffer' it's now just 'setArgumentBuffer'.

Thanks Dan! I was actually missing to tell the encoder which buffer to encode into.


But is there any convienience function to make the buffer with correct size?

There is no "convenience" function as such. You just create your argument buffer using argumentEncoder.encodedLength as follows


cameraStateArgumentBuffer = device.makeBuffer(length:argumentEncoder.encodedLength options:0)