I am trying to let a neural net run on metal. The basic idea is that of data duplication. Each gpu thread runs one version of the net for random data points.
I have written other shaders that work fine.
I also tried my code in a c++ command line app. No errors there. There is also no compile error.
I used the apple documentation to convert to metal c++, since not everything from c++11 is supported.
It crashes after it loads the kernel function and when it tries to assign newComputePipelineStateWithFunction
to the metal device. This means there is a problem with the code that isn't caught at compile time.
MCVE:
kernel void net(const device float *inputsVector [[ buffer(0) ]], // layout of net *
uint id [[ thread_position_in_grid ]]) {
uint floatSize = sizeof(tempFloat);
uint inputsVectorSize = sizeof(inputsVector) / floatSize;
float newArray[inputsVectorSize];
float test = inputsVector[id];
newArray[id] = test;
}
Update
It has everything to do with dynamic arrays.
Since it fails to create the pipeline state and doesn't crash running the actual shader it must be a coding issue. Not an input issue.
Assigning values from a dynamic array to a buffer makes it fail.
The real problem: It is a memory issue!
To all the people saying that it was a memory issue, you were right! Here is some pseudo code to illustrate it. Sorry that it is in "Swift" but easier to read. Metal Shaders have a funky way of coming to life. They are first initialised without values to get the memory. It was this step that failed because it relied on a later step: setting the buffer.
It all comes down to which values are available when. My understanding of newComputePipelineStateWithFunction
was wrong. It is not simply getting the shader function. It is also a tiny step in the initialising process.
class MetalShader {
// buffers
var aBuffer : [Float]
var aBufferCount : Int
// step One : newComputePipelineStateWithFunction
memory init() {
// assign shader memory
// create memory for one int
let aStaticValue : Int
// create memory for one int
var aNotSoStaticValue : Int // this wil succeed, assigns memory for one int
// create memory for 10 floats
var aStaticArray : [Float] = [Float](count: aStaticValue, repeatedValue: y) // this will succeed
// create memory for x floats
var aDynamicArray : [Float] = [Float](count: aBuffer.count, repeatedValue: y) // this will fail
var aDynamicArray : [Float] = [Float](count: aBufferCount, repeatedValue: y) // this will fail
let tempValue : Float // one float from a loop
}
// step Two : commandEncoder.setBuffer()
assign buffers (buffers) {
aBuffer = cpuMemoryBuffer
}
// step Three : commandEncoder.endEncoding()
actual init() {
// set shader values
let aStaticValue : Int = 0
var aNotSoStaticValue : Int = aBuffer.count
var aDynamicArray : [Float] = [Float](count: aBuffer.count, repeatedValue: 1) // this could work, but the app already crashed before getting to this point.
}
// step Four : commandBuffer.commit()
func shaderFunction() {
// do stuff
for i in 0..<aBuffer.count {
let tempValue = aBuffer[i]
}
}
}
Fix:
I finally realised that buffers are technically dynamic arrays and instead of creating arrays inside the shader, I could also just add more buffers. This obviously works.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With