Ios MTKView绘图性能 我想做什么
我正在尝试使用金属视图在摄影机提要上显示过滤器:Ios MTKView绘图性能 我想做什么,ios,swift,xcode,metal,metal-performance-shaders,Ios,Swift,Xcode,Metal,Metal Performance Shaders,我正在尝试使用金属视图在摄影机提要上显示过滤器:MTKView。我正在密切关注苹果公司的示例代码——利用TrueDepth摄像头数据增强实时视频() 到目前为止我所拥有的 以下代码非常有用(主要从上述示例代码中解释): 注释 之所以使用MTKViewDelegate而不是子类化MTKView是因为当它被子类化时,在主线程上调用了draw调用。对于上面所示的委托方法,似乎每个循环都有一个不同的金属相关线程调用。上述方法似乎提供了更好的性能 关于上述更新方法的CIFilter用法的详细信息必须进行
MTKView
。我正在密切关注苹果公司的示例代码——利用TrueDepth摄像头数据增强实时视频()
到目前为止我所拥有的
以下代码非常有用(主要从上述示例代码中解释):
注释
- 之所以使用
而不是子类化MTKViewDelegate
是因为当它被子类化时,在主线程上调用了draw调用。对于上面所示的委托方法,似乎每个循环都有一个不同的金属相关线程调用。上述方法似乎提供了更好的性能MTKView
- 关于上述更新方法的
用法的详细信息必须进行编辑。所有这一切都是一条沉重的过滤链。不幸的是,这些过滤器没有任何调整的余地CIFilter
UIScrollview
gets看起来很慢,而且不稳定
目标
调整Metal视图以减轻CPU的负担,并在主线程上轻松处理,为UI的其余部分留下足够的资源
根据上面的图形,命令缓冲区的准备都是在CPU中完成的,直到出现并提交(?)。有没有办法把它从CPU上卸下来
任何提示,反馈,提示等,以提高绘图效率将不胜感激
您可以做以下几件事来提高性能:
- 直接渲染到视图的可绘制视图中,而不是渲染到纹理中,然后再次渲染以将该纹理渲染到视图中
- 使用最新的
API将实际纹理检索延迟到视图实际渲染到的时刻(即核心图像完成时)CIRenderDestination
绘图(视图:MTKView)
,针对您的情况进行了修改:
public func draw(in view: MTKView) {
if let currentDrawable = view.currentDrawable,
let commandBuffer = self.commandQueue.makeCommandBuffer() {
let drawableSize = view.drawableSize
// optional: scale the image to fit the view
let scaleX = drawableSize.width / image.extent.width
let scaleY = drawableSize.height / image.extent.height
let scale = min(scaleX, scaleY)
let scaledImage = previewImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
// optional: center in the view
let originX = max(drawableSize.width - scaledImage.extent.size.width, 0) / 2
let originY = max(drawableSize.height - scaledImage.extent.size.height, 0) / 2
let centeredImage = scaledImage.transformed(by: CGAffineTransform(translationX: originX, y: originY))
// create a render destination that allows to lazily fetch the target texture
// which allows the encoder to process all CI commands _before_ the texture is actually available;
// this gives a nice speed boost because the CPU doesn’t need to wait for the GPU to finish
// before starting to encode the next frame
let destination = CIRenderDestination(width: Int(drawableSize.width),
height: Int(drawableSize.height),
pixelFormat: view.colorPixelFormat,
commandBuffer: commandBuffer,
mtlTextureProvider: { () -> MTLTexture in
return currentDrawable.texture
})
let task = try! self.context.startTask(toRender: centeredImage, to: destination)
// bonus: you can Quick Look the task to see what’s actually scheduled for the GPU
commandBuffer.present(currentDrawable)
commandBuffer.commit()
// optional: you can wait for the task execution and Quick Look the info object to get insights and metrics
DispatchQueue.global(qos: .background).async {
let info = try! task.waitUntilCompleted()
}
}
}
如果这仍然太慢,您可以尝试在创建
CIContext
时设置priorityRequestLow
cicontextext
以告知核心图像以低优先级渲染。正确的做法是使用仪器进行测量。也就是说,有两个明显的问题:在每次绘制时都创建纹理。相反,您应该在开始时创建一个小的纹理池,为每个绘制抓取一个,并在命令缓冲区完成后将其返回到该池。同样,不应在每次绘制时创建队列。您通常应该在应用程序设置中创建一个队列,并在整个过程中使用它。由于要手动(从后台线程)调用draw()
,因此不要设置enableSetNeedsDisplay
。似乎不需要清除framebufferOnly
。对于队列,我创建self.metalCommandQueue=self.metalDevice!。初始化时生成命令队列()
。那是不对的?哦,你是对的。对不起,我看错了。我被你使用guard
的行为愚弄了,可能会报告你无法在draw方法中创建队列。因此对于最简单的设置:framebufferOnly=true和enableSetNeedsDisplay=false?是的,但纹理创建可能是一个更大的因素。这提高了FPS!但是,它似乎仍然会降低主线程/CPU的速度,从而导致UI其余部分的运行缓慢。在绘图调用之前,是否可以在update()中完成这些工作?请注意,您的UI绘图也在GPU上进行。因此,当GPU太忙时,UI将挂起。在update()
或draw()
中执行实际的核心图像渲染并不重要。您可以降低捕获设备的FPS,这样系统就不会被无法处理的帧淹没。还可以使用self.metalBufferView?.setNeedsDisplay()
而不是.draw()
来允许UIKit在适当的时候绘制视图。如果过滤器在draw()之外运行,它们是否应该运行?或者内部会提供更好的性能?这无关紧要,因为在实际调用CIContext
的一个相关绘图函数之前,CIFilter
永远不会运行(startTask(本例中为toRender:to:)
),这需要在draw()内部进行
@Gizmodo您是否尝试过在上下文中设置priorityRequestLow
?
public func draw(in view: MTKView) {
if let currentDrawable = view.currentDrawable,
let commandBuffer = self.commandQueue.makeCommandBuffer() {
let drawableSize = view.drawableSize
// optional: scale the image to fit the view
let scaleX = drawableSize.width / image.extent.width
let scaleY = drawableSize.height / image.extent.height
let scale = min(scaleX, scaleY)
let scaledImage = previewImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
// optional: center in the view
let originX = max(drawableSize.width - scaledImage.extent.size.width, 0) / 2
let originY = max(drawableSize.height - scaledImage.extent.size.height, 0) / 2
let centeredImage = scaledImage.transformed(by: CGAffineTransform(translationX: originX, y: originY))
// create a render destination that allows to lazily fetch the target texture
// which allows the encoder to process all CI commands _before_ the texture is actually available;
// this gives a nice speed boost because the CPU doesn’t need to wait for the GPU to finish
// before starting to encode the next frame
let destination = CIRenderDestination(width: Int(drawableSize.width),
height: Int(drawableSize.height),
pixelFormat: view.colorPixelFormat,
commandBuffer: commandBuffer,
mtlTextureProvider: { () -> MTLTexture in
return currentDrawable.texture
})
let task = try! self.context.startTask(toRender: centeredImage, to: destination)
// bonus: you can Quick Look the task to see what’s actually scheduled for the GPU
commandBuffer.present(currentDrawable)
commandBuffer.commit()
// optional: you can wait for the task execution and Quick Look the info object to get insights and metrics
DispatchQueue.global(qos: .background).async {
let info = try! task.waitUntilCompleted()
}
}
}