我正在使用 Metal 性能着色器( MPSImageHistogram
)来计算 MTLBuffer
中的某些内容我抓取、执行计算,然后通过 MTKView
显示。 MTLBuffer
着色器的输出很小(~4K 字节)。所以我分配一个新的 MTLBuffer
每个渲染 channel 的对象,并且每个视频帧每秒至少有 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)
...
...
}
我的问题 -
每次使用
device.makeBuffer(..)
创建一个新的缓冲区可以吗? ,或者更好地静态分配 很少的缓冲区并实现重用这些缓冲区?如果重用更好,我们如何同步这些缓冲区上的 CPU/GPU 数据写入/读取?另一个不相关的问题,可以画入
MTKView
吗?非主线程上的结果?或MTKView
绘制必须仅在主线程中(即使我读到 Metal 是真正的多线程)?
最佳答案
分配有点昂贵,因此我建议使用可重用的缓冲区方案。我首选的方法是保留一个可变的缓冲区数组(队列),当使用它的命令缓冲区完成时(或者在您的情况下,在 CPU 上读回结果之后)将缓冲区排入队列,然后分配当队列为空并且需要编码更多工作时使用新缓冲区。在稳定状态下,假设您的帧及时完成,您会发现此方案很少会分配超过 2-3 个缓冲区。如果您需要此方案是线程安全的,则可以使用互斥体(使用
dispatch_semaphore
实现)保护对队列的访问。只要遵循标准的多线程预防措施,您就可以使用另一个线程对渲染工作进行编码,将其绘制到由
MTKView
提供的可绘制对象中。请记住,虽然命令队列是线程安全的(从某种意义上说,您可以同时从同一队列创建和编码多个命令缓冲区),但命令缓冲区本身和编码器却不是。我建议您分析单线程情况,并且仅在绝对必要时才引入多线程的复杂性。
关于ios - MTL缓冲区分配+CPU/GPU同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50213499/