CAMetalLayer
的{{3}}表示属性presentsWithTransaction
为:
一个布尔值,用于确定图层是否使用核心动画事务显示其内容
由于我正在使用UIKit来驱动一些金属动画(类似于Apple在此The Apple docs中建议的OpenGL方法),我假设这是启用它的正确时间。我有一个金属“背景”视图,覆盖了一些UIKit组件(也有动画),所以这听起来非常像适用的用例:
默认情况下,[
.presentsWithTransaction
]为false:CAMetalLayer
尽可能快地向显示器显示渲染传递的输出,并与任何Core Animation事务异步显示。但是,如果您的游戏或应用程序结合了金属和核心动画内容,则无法保证您的金属内容将与核心动画内容在同一帧中到达。例如,如果您的应用在CAMetalLayer
的顶部绘制UIKit内容(例如具有目标位置和时间的标签)并且需要同步两个域,则可能会出现问题。
当然,如果未启用该设置,滚动会显得不稳定。启用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()
}
:
public class PIArrays {
public boolean[] pairwiseContainsDividableBy(final int[] a, final int[] b,
final int divisor) {
boolean[] result = new boolean [a.length];
for(int i = 0; i < a.length; i++){
if(a[i]%divisor == 0 && b[i]%divisor == 0){
result[i] = true;
}else{
result[i] = false;
}
}
return result; }
}
此方法似乎保持同步,但会导致一些可怕的CPU挂起(2秒或更长时间),尤其是当应用程序在后台运行后变为活动状态时。
有没有办法让两全其美?
编辑:2018年12月9日: 虽然上面描述的第一种方法看起来确实是优先的,但如果主线程上的CPU使用率出现峰值,它仍会导致频繁的去同步 - 这在大多数情况下是不可避免的。
当绘制循环变得缺乏drawable时,你可以知道这种情况何时发生。这会导致连锁效应,这意味着下一帧的可绘制也会延迟。在“金属工具”面板中,这会导致一系列“线程被阻塞等待下一个可绘制”警告。
通过上面的设计,当Metal被阻塞等待drawable时 - 主线程也是如此。现在触摸事件被延迟,导致平移手势的独特口吃模式 - 即使应用程序仍然在理论上以完整的60fps运行,阻塞似乎会影响报告触摸事件的节奏 - 导致抖动效果。
随后的CPU峰值可以将事情重新排列,应用程序将开始正常运行。
编辑:2018年12月10日: 这是一个展示问题的小例子项目。创建一个新的Xcode项目副本并粘贴两个swift文件的内容(为金属着色器文件添加一个新文件)并在设备上运行:
答案 0 :(得分:3)
编辑2018年12月9日: 我正在从此答案中删除可接受的答案名称,因为此错误似乎仍然很大。尽管以下内容使错误发生的可能性降低了,但仍然确实发生了。原始问题中的更多信息。
原始答案: 这似乎对我有用:
func draw(in view: MTKView) {
updateState() // update positions, etc.
guard let commandBuffer = commandQueue.makeCommandBuffer() else { return }
autoreleasepool { render(with: commandBuffer, in: view) } // drawing code
commandBuffer.commit()
commandBuffer.waitUntilScheduled()
view.currentDrawable?.present()
}
.presentsWithTransaction
上的 MTKView
也设置为true
这个想法是等到最后一次调用currentDrawable
时才出现,这是在文档中建议的某个地方,但是我不记得现在在哪里。我认为至少应该在.waitUntilScheduled()
之后调用它。