合并多个 MTKView/CAMetalLayer 显示

Combine multiple MTKView/CAMetalLayer display

我正在使用多个 MTKView 在屏幕上显示不同的内容以及正常的 UIView(用于显示 UI)。我想将这些 MTKViews 的呈现与同一个时钟同步。有没有办法同步这些MTKViews的呈现?原则上,我可以将这些视图的布局组合到一个 MTKView 中,但这会破坏代码的模块化,并且不确定我是否可以通过如此多的工作量来提高性能。

一种在大多数情况下应该有效的简单方法是计算您希望框架绘制的时间,并使用 MTLCommandBufferpresent(_:atTime:) 方法而不是 present(_:)方法。


为了更好地控制,了解命令缓冲区的 present... 方法做什么和不做什么会有所帮助。他们将任何命令编码到缓冲区中。如文档所述,他们基本上只是为自己添加一个预定的处理程序,该处理程序在可绘制对象上调用 present

如果您小心谨慎,可以安排以不太涉及命令缓冲区的方式呈现可绘制对象。

但是,使用预定处理程序的命令缓冲区有意义吗?它不应该使用 completed 处理程序吗?毕竟,你想要显示完成的渲染,对吧?

好吧,可绘制对象很擅长展示自己。 present 方法不会立即出现。预定命令可能渲染或写入其纹理的可绘制轨道。调用 present 时,会安排可绘制对象尽快在屏幕上绘制自己 在所有此类命令完成后 。 (请注意,这并不意味着命令缓冲区本身已完成。可能还有其他不涉及尚未完成的可绘制对象纹理的命令。)

这为同步多个可绘制对象的呈现提供了挑战和机遇。挑战在于,虽然您可以控制在每个可绘制对象上调用 present 的时间,但这并不一定会同步它们的实际显示,因为每个可绘制对象都会在调用 present 和所有命令后尽快显示涉及其纹理的部分已完成,最后一部分对于不同的可绘制对象可能发生在不同的时间。

解决此问题的一种可能方法是向主可绘制对象添加呈现的处理程序。处理程序将在其他 3 个可绘制对象上调用 present。在调度所有命令缓冲区后,在主可绘制对象上调用 present。您可以使用调度组来确定何时安排所有命令缓冲区。为每个命令缓冲区输入一次组,并为每个离开该组的命令缓冲区添加一个预定的处理程序。然后在 master 存在的组上设置一个通知块。这种技术可能无法实现完美的同步,因为在实际呈现主可绘制对象和调用呈现的处理程序之间存在延迟,然后是呈现其他可绘制对象的延迟。

另一种可能的方法是将所有 CAMetalLayerpresentsWithTransaction 属性 设置为 true。然后,当需要呈现时,在每个命令缓冲区上调用 waitUntilScheduled,然后在每个可绘制对象上调用 present。 (不要使用命令缓冲区的 present... 方法。)这将保证所有可绘制对象都将在同一个 Core Animation 事务期间呈现 - 即同步。

您可以使用 presentsWithTransaction

  1. 在 MTKView 中设置 presentsWithTransaction = true

  2. 更改 commandBuffer 提交样式

     public func draw(in view: MTKView) {
    
     ...
    
     commandBuffer?.commit()
     commandBuffer?.waitUntilScheduled()
     view.currentDrawable?.present() }
    

现在所有金属视图将同步显示。 对我有用。