MTLBuffer分配+ CPU / GPU同步

时间:2018-05-07 11:48:20

标签: ios metal metalkit metal-performance-shaders mtlbuffer

我正在使用金属性能着色器(MPSImageHistogram)来计算我抓取的MTLBuffer中的内容,执行计算,然后通过MTKView显示。着色器的MTLBuffer输出很小(~4K字节)。所以我为每个渲染过程分配一个新的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绘制必须只在主线程中(即使我读过Metal是真正的多线程)?

2 个答案:

答案 0 :(得分:1)

  1. 如果是少量数据(4K以下),您可以使用setBytes():https://developer.apple.com/documentation/metal/mtlcomputecommandencoder/1443159-setbytes
  2. 这可能比每帧分配一个新缓冲区更快/更好。你也可以使用三重缓冲的方法,以便连续的帧'访问缓冲区不会干扰。 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

    这实际上就像教程的第三部分,但它是显示三重缓冲设置的部分,在#34;重用统一缓冲区"。

答案 1 :(得分:1)

  1. 分配有点贵,所以我建议使用可重复使用的缓冲方案。我这样做的首选方法是保留缓冲区的可变数组(队列),在使用缓冲区的命令缓冲区完成时(或者在您的情况下,在CPU上读回结果后)将缓冲区排入队列,并分配队列为空时需要编码更多工作的新缓冲区。在稳定状态下,假设您的帧及时完成,您会发现此方案很少会分配超过2-3个缓冲区。如果您需要此方案是线程安全的,则可以使用互斥锁保护对队列的访问(使用dispatch_semaphore实现)。

  2. 只要您遵循标准的多线程预防措施,您就可以使用另一个线程对渲染工作进行编码,该渲染工作将绘制为由MTKView提供的可绘制工具。请记住,虽然命令队列是线程安全的(在某种意义上,您可以同时创建和编码来自同一队列的多个命令缓冲区),但命令缓冲区本身和编码器不是。我建议你分析单线程案例,只在必要时引入多线程的复杂性。