iOS Metal:如何正确捕获GPU帧?

sar*_*ati 3 xcode profiling compute-shader ios metal

我正在尝试通过捕获 GPU 帧来分析 Metal 内核。在具有 Metal runloop 的应用程序中,我会单击调试区域中的“相机按钮”,但是每个应用程序生命周期我只调度内核一次,因此我无法单击“相机按钮”(它保持灰色)。

\n\n

因此,我尝试通过在第一次调用之前使用“捕获 GPU 帧”操作设置断点来解决此问题mQueue.insertDebugCaptureBoundary()(请参阅下面的代码)。

\n\n

我期望发生的事情是这样的\ xe2\x80\x93 ,即每个内核函数的执行持续时间的概述,以及执行各行内核函数所花费的时间百分比。

\n\n

实际发生的情况是:我很少得到所描述的预期分析概述。大多数时候(大约 95% 的时间)我没有得到这样的分析概述,而是在构建并运行应用程序后发生以下情况之一:

\n\n
    \n
  • 没有显示“调试 GPU 帧”窗口 \xe2\x80\x93,只有 XCode 的状态栏更改为“捕获 GPU 帧”并带有活动微调器;插图在这里
  • \n
  • 显示“调试 GPU 帧”窗口,但是没有显示任何编码命令,因此没有显示执行时间,并且不存在 GPU 对象浏览器(我所说的对象是指 MTLBuffers 和 MTLTextures);插图在这里
  • \n
  • 弹出一个无标题的 XCode 窗口,显示“超时 (5)”,并且没有其他任何反应;插图在这里
  • \n
\n\n

下面的代码显示了我的问题的简化示例(如果您想知道;不,我在 ViewController 中没有计算逻辑 - 下面的代码只是一个玩具示例;))。

\n\n
class ViewController : UIViewController {  \n  // initialize Metal, create buffers, etc.  \n\n  override func viewDidLoad() {  \n    tick() // called exactly once \xe2\x80\x93 how to profile the the kernels?  \n  }  \n\n  func tick() {  \n      // On this (empty) line, there\'s set a breakpoint with the action "Capture GPU Frame"  \n      mQueue.insertDebugCaptureBoundary() // start frame capture here  \n      let cmdBuff = mQueue.commandBuffer()  \n      let compEnc = cmdBuff.computeCommandEncoder()  \n\n      // ------- Dispatch several kernels -------  \n      compEnc.setComputePipelineState(foo)  \n      compEnc.setBuffer(..., offset: 0, atIndex: 0)  \n      compEnc.setBuffer(..., offset: 0, atIndex: 1)  \n      // ...  \n      compEnc.dispatchThreadgroups(..., ...)  \n\n\n      compEnc.setComputePipelineState(bar)  \n      compEnc.setBuffer(..., offset: 0, atIndex: 0)  \n      compEnc.setBuffer(..., offset: 0, atIndex: 1)  \n      // ...  \n      compEnc.dispatchThreadgroups(..., ...)  \n      // ------- /Dispatch several kernels -------  \n\n      compEnc.endEncoding()  \n      cmdBuff.commit()  \n      cmdBuff.waitUntilCompleted()  \n\n      mQueue.insertDebugCaptureBoundary() // end the frame capture here  \n  }  \n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

sar*_*ati 5

在 iOS 11 中,我们可以使用MTLCaptureManager来可靠地捕获计算内核的一次调用。

当您运行下面的示例时,它会开始捕获。您可以通过 Xcode 的“GPU 捕获按钮”(参见图片)或通过 的MTLCaptureManager方法以编程方式停止捕获stopCapture()

GPU 捕获按钮

// 1. First create the Metal device and command queue
let dev   = MTLCreateSystemDefaultDevice()!
let queue = dev.makeCommandQueue()!

// 2. Access the shared MTLCaptureManager and start capturing
let capManager = MTLCaptureManager.shared()
capManager.startCapture(commandQueue: queue)

// 3. Encode commands into the queue
let cmdbuff = queue.makeCommandBuffer()!
let enc     = cmdbuff.makeComputeCommandEncoder()!

// encode your kernel

enc.endEncoding()
cmdbuff.commit()
Run Code Online (Sandbox Code Playgroud)