Распределение MTLBuffer + синхронизация CPU / GPU - PullRequest
0 голосов
/ 07 мая 2018

Я использую металлический шейдер производительности (MPSImageHistogram) для вычисления чего-то в MTLBuffer, который я беру, выполняю вычисления, а затем отображаю через MTKView. Вывод MTLBuffer из шейдера мал (~ 4 Кбайт). Поэтому я выделяю новый объект MTLBuffer для каждого прохода рендеринга, и для каждого видеокадра существует не менее 30 операций рендеринга в секунду.

calculation = MPSImageHistogram(device: device, histogramInfo: &histogramInfo)
let bufferLength = calculation.histogramSize(forSourceFormat: MTLPixelFormat.bgra8Unorm)
let buffer = device.makeBuffer(length: bufferLength, options: .storageModeShared)
let commandBuffer = commandQueue?.makeCommandBuffer()

calculation.encode(to: commandBuffer!, sourceTexture: metalTexture!, histogram: buffer!, histogramOffset: 0)
commandBuffer?.commit()

commandBuffer?.addCompletedHandler({ (cmdBuffer) in
    let dataPtr = buffer!.contents().assumingMemoryBound(to: UInt32.self)
    ...
    ...

}

Мои вопросы -

  1. Можно ли каждый раз создавать новый буфер, используя device.makeBuffer(..), или лучше статически распределять несколько буферов и реализовать повторное использование этих буферов? Если повторное использование лучше, что мы делаем для синхронизации записи / чтения данных CPU / GPU в этих буферах?

  2. Еще один не связанный вопрос, можно ли рисовать MTKView результаты в неосновной ветке? Или MTKView ничья должна быть только в основном потоке (даже если я читаю, что Металл действительно многопоточный)?

Ответы [ 2 ]

0 голосов
/ 08 мая 2018
  1. Выделения довольно дороги, поэтому я бы порекомендовал схему буферов многократного использования. Мой предпочтительный способ сделать это - сохранить изменяемый массив (очередь) буферов, поставить в очередь буфер после завершения буфера команд, который его использовал (или, в вашем случае, после того, как вы прочитали результаты на ЦП), и выделить новый буфер, когда очередь пуста и вам нужно кодировать больше работы. В установившемся режиме вы обнаружите, что эта схема редко выделяет всего более 2-3 буферов, при условии, что ваши кадры завершаются своевременно. Если вам требуется, чтобы эта схема была поточно-ориентированной, вы можете защитить доступ к очереди с помощью мьютекса (реализованного с помощью dispatch_semaphore).

  2. Вы можете использовать другой поток для кодирования работы рендеринга, которая рисуется в рисованной форме с MTKView, если вы соблюдаете стандартные меры предосторожности при многопоточности. Помните, что, хотя очереди команд являются поточно-ориентированными (в том смысле, что вы можете одновременно создавать и кодировать несколько буферов команд из одной и той же очереди), сами буферы команд, а кодировщики - нет. Я бы посоветовал вам профилировать однопоточный случай и вводить усложнение многопоточности, только если / когда это абсолютно необходимо.

0 голосов
/ 08 мая 2018
  1. Если это небольшой объем данных (до 4K), вы можете использовать setBytes (): https://developer.apple.com/documentation/metal/mtlcomputecommandencoder/1443159-setbytes

Это может быть быстрее / лучше, чем выделять новый буфер каждый кадр. Вы также можете использовать подход с тройной буферизацией, чтобы доступ последовательных кадров к буферу не мешал. https://developer.apple.com/library/content/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/TripleBuffering.html

Этот туториал покажет, как настроить тройную буферизацию для рендеринга: https://www.raywenderlich.com/146418/metal-tutorial-swift-3-part-3-adding-texture

Это на самом деле похоже на третью часть руководства, но именно эта часть показывает настройку тройной буферизации в разделе «Повторное использование унифицированных буферов».

...