NewComputePipelineStateWithFunction не удалось

Я пытаюсь позволить нейронной сети работать на металле. Основная идея заключается в дублировании данных. Каждый поток gpu запускает одну версию сети для случайных точек данных.

Я написал другие шейдеры, которые отлично работают.

Я также пробовал свой код в приложении командной строки С++. Ошибок нет. Также нет ошибки компиляции.

Я использовал документацию apple для преобразования в metal С++, так как поддерживается не все, начиная с С++ 11.

Он вылетает после загрузки функции ядра и при попытке назначить newComputePipelineStateWithFunction на металлическое устройство. Это означает, что есть проблема с кодом, который не был обнаружен во время компиляции.

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;

}

Обновление

Он имеет все, что связано с динамическими массивами.

Поскольку он не создает состояние конвейера и не запускает фактический шейдер, он должен быть проблемой кодирования. Не проблема ввода.

Назначение значений из динамического массива в буфер заставляет его сбой.

Ответ 1

Реальная проблема: Это проблема с памятью!

Для всех людей, говорящих, что это проблема памяти, вы были правы! Вот несколько псевдокодов, чтобы проиллюстрировать это. Извините, что он находится в "Swift", но его легче читать. У металлических шейдеров есть забавный способ оживить. Они сначала инициализируются без значений, чтобы получить память. Произошел этот шаг, потому что он полагался на более поздний шаг: установка буфера.

Все зависит от того, какие значения доступны, когда. Мое понимание newComputePipelineStateWithFunction было неправильным. Это не просто функция шейдера. Это также крошечный шаг в процессе инициализации.

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:

Наконец я понял, что буферы представляют собой технически динамические массивы, и вместо создания массивов внутри шейдера я мог бы просто добавить больше буферов. Это очевидно работает.

Ответ 2

Я думаю, что ваша проблема связана с этой строкой:

uint schemeVectorSize = sizeof(schemeVector) / uintSize;

Здесь schemeVector динамический, поэтому как в классическом С++, вы не можете использовать sizeof в динамическом массиве для получения количества элементов, sizeof будет работать только с массивами, которые вы определили локально/статически в коде металлического шейдера.

Представьте себе, как он работает внутри страны: во время компиляции Metal компилятор должен преобразовать вызов sizeof в константу... но он не может, поскольку schemeVector является параметром вашего шейдера и, следовательно, может имеют любой размер...

Итак, для меня решением было бы вычислить schemeVectorSize в части кода С++/ObjectiveC/Swift вашего кода и передать его как параметр шейдеру (как единую форму в терминологии OpenGLES...).