Apple 文档CAMetalLayer
声明该属性为presentsWithTransaction
:
一个布尔值,用于确定图层是否使用核心动画事务呈现其内容
由于我正在使用 UIKit 来驱动一些金属动画(类似于 Apple 在此WWDC 2012 会话中为 OpenGL 建议的方法),我假设这是启用它的正确时间。我有一个金属“背景”视图,上面覆盖着一些 UIKit 组件(它们也是动画),所以这听起来很像适用的用例:
默认情况下 [
.presentsWithTransaction
] 为 false:CAMetalLayer
尽可能快地将渲染过程的输出显示到显示器,并且与任何 Core Animation 事务异步。但是,如果您的游戏或应用程序结合了 Metal 和 Core Animation 内容,则不能保证您的 Metal 内容将与您的 Core Animation 内容在同一帧中到达。例如,如果您的应用程序在您的顶部绘制 UIKit 内容(例如带有目标位置和时间的标签)CAMetalLayer
并且两个域需要同步,这可能是一个问题。
当然,如果没有启用该设置,滚动会显得生涩。presentsWithTransaction
启用后,我取得了一些有限的成功,但我在启用设置的情况下尝试的两条路线都不是完美的。
我尝试的第一种方法遵循文档中的说明presentsWithTransaction
。因此,在我的内部,MTKViewDelegate
我有以下方法:
func draw(in view: MTKView) {
guard
let commandBuffer = commandQueue.makeCommandBuffer(),
let drawable = view.currentDrawable
else { return }
updateState(device: device, library: library) // update positions, etc.
render(with: commandBuffer, in: view) // drawing code
commandBuffer.commit()
commandBuffer.waitUntilScheduled()
drawable.present()
}
这大部分工作正常 - 但完全离开设置也是如此。它有在某些点不同步的趋势,导致通过UIScrollView
例如驱动滚动动画的特征性颤抖。的整个想法presentsWithTransaction
是为了避免这种情况,所以也许我在这里做错了什么。
第二种方法利用addScheduledHandler
命令缓冲区:
func draw(in view: MTKView) {
guard
let commandBuffer = commandQueue.makeCommandBuffer(),
let drawable = view.currentDrawable
else { return }
updateState(device: device, library: library) // update positions, etc.
render(with: commandBuffer, in: view) // drawing code
commandBuffer.addScheduledHandler { _ in
DispatchQueue.main.async { drawable.present() }
}
commandBuffer.commit()
}
这种方法似乎保持同步,但会导致一些可怕的 CPU 挂起(2 秒或更长时间),尤其是当应用程序在后台后变为活动状态时。
有什么办法可以两全其美?
编辑:2018 年 12 月 9 日: 虽然上面描述的第一种方法似乎是优先的,但如果主线程上的 CPU 使用率出现峰值,它仍然会导致频繁的去同步——这在大多数情况下是不可避免的。
当绘制循环变得缺乏可绘制对象时,您可以判断何时发生这种情况。这会产生连锁效应,这意味着下一帧的可绘制对象也会被延迟。在 Metal Instruments 面板中,这会导致一系列“线程阻塞等待下一个可绘制”警告。
在上面的设计中,由于 Metal 在等待可绘制对象时被阻塞 - 主线程也是如此。现在,触摸事件变得延迟,导致平移手势出现明显的断断续续模式——即使应用程序理论上仍以 60 fps 的速度运行,阻塞似乎会影响报告触摸事件的节奏——从而导致抖动效果。
随后的 CPU 峰值可能会使事情恢复正常,应用程序将开始正常运行。
编辑:2018 年 12 月 10 日: 这是一个演示该问题的小示例项目。创建一个新的 Xcode 项目,复制并粘贴两个 swift 文件的内容(为金属着色器文件添加一个新文件)并在设备上运行:
https://gist.github.com/tcldr/ee7640ccd97e5d8810af4c34cf960284