1

我正在使用金属性能着色器(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 是真正的多线程)?

4

2 回答 2

2
  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

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

于 2018-05-07T21:21:01.733 回答
2
  1. 分配有点贵,所以我推荐一个可重用的缓冲方案。我首选的方法是保留一个可变的缓冲区数组(队列),当使用它的命令缓冲区完成时(或者在你的情况下,在你读回 CPU 上的结果之后)将缓冲区排队,并分配当队列为空并且您需要编码更多工作时,一个新的缓冲区。在稳定状态下,假设您的帧及时完成,您会发现此方案很少会分配超过 2-3 个缓冲区的总数。如果您需要此方案是线程安全的,则可以使用互斥锁(使用 a 实现dispatch_semaphore)保护对队列的访问。

  2. MTKView只要您遵循标准的多线程预防措施,您就可以使用另一个线程来编码绘制到由 提供的可绘制对象中的渲染工作。请记住,虽然命令队列是线程安全的(从某种意义上说,您可以同时从同一个队列创建和编码多个命令缓冲区),但命令缓冲区本身和编码器却不是。我建议您分析单线程情况,并且仅在绝对必要时才引入多线程的复杂性。

于 2018-05-08T03:04:35.770 回答