我正在尝试通过捕获GPU帧来分析Metal内核。在具有Metal循环运行的应用程序中,我会单击“调试区域”中的“相机按钮”,但由于我每个应用程序生命周期仅调度一次内核,因此我无法单击“相机按钮”(它保持灰色)。
因此,我尝试通过在第一次调用之前设置带有“捕获GPU帧”操作的断点来解决此问题(请参见下面的代码)。
我期望发生的是类似于这样的事情-即每个内核函数执行持续时间的概述,并注明了执行内核函数各行所花费的时间百分比。
实际上发生的是:我很少得到所描述的预期分析概述。大多数情况下(约95%的时间),在构建和运行应用程序后,我不会获得此类分析概述,而是会出现以下情况之一:
因此,我尝试通过在第一次调用之前设置带有“捕获GPU帧”操作的断点来解决此问题(请参见下面的代码)。
我期望发生的是类似于这样的事情-即每个内核函数执行持续时间的概述,并注明了执行内核函数各行所花费的时间百分比。
实际上发生的是:我很少得到所描述的预期分析概述。大多数情况下(约95%的时间),在构建和运行应用程序后,我不会获得此类分析概述,而是会出现以下情况之一:
- 没有显示“调试GPU帧”窗口-只有XCode的状态栏更改为“捕获GPU帧”,并且有活动旋转器;插图在这里。
- 显示了“调试GPU帧”窗口,但没有显示编码命令,因此不显示执行时间,并且没有GPU对象浏览器(通过对象我指的是MTLBuffers和MTLTextures);插图在这里。
- 弹出一个无标题的XCode窗口,上面写着“超时(5)”,然后什么都没有发生;插图在这里。
class ViewController : UIViewController {
// initialize Metal, create buffers, etc.
override func viewDidLoad() {
tick() // called exactly once – how to profile the the kernels?
}
func tick() {
// On this (empty) line, there's set a breakpoint with the action "Capture GPU Frame"
mQueue.insertDebugCaptureBoundary() // start frame capture here
let cmdBuff = mQueue.commandBuffer()
let compEnc = cmdBuff.computeCommandEncoder()
// ------- Dispatch several kernels -------
compEnc.setComputePipelineState(foo)
compEnc.setBuffer(..., offset: 0, atIndex: 0)
compEnc.setBuffer(..., offset: 0, atIndex: 1)
// ...
compEnc.dispatchThreadgroups(..., ...)
compEnc.setComputePipelineState(bar)
compEnc.setBuffer(..., offset: 0, atIndex: 0)
compEnc.setBuffer(..., offset: 0, atIndex: 1)
// ...
compEnc.dispatchThreadgroups(..., ...)
// ------- /Dispatch several kernels -------
compEnc.endEncoding()
cmdBuff.commit()
cmdBuff.waitUntilCompleted()
mQueue.insertDebugCaptureBoundary() // end the frame capture here
}
}
tick();tick();tick();
。(未经测试。) - sarasvati