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

3
我正在尝试通过捕获GPU帧来分析Metal内核。在具有Metal循环运行的应用程序中,我会单击“调试区域”中的“相机按钮”,但由于我每个应用程序生命周期仅调度一次内核,因此我无法单击“相机按钮”(它保持灰色)。
因此,我尝试通过在第一次调用之前设置带有“捕获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  
  }  

}

@warrenm Xcode 7.3 (7D175)。测试设备为运行iOS 9.3.5(13G36)的iPhone 6S。 - sarasvati
可能没有什么区别,但你尝试过使用Xcode 7.3.1(7D1012)或Xcode 8 beta 6(8S201h)吗? - warrenm
@warrenm 感谢你的提示。目前我正在下载Xcode 8,如果新版本的Xcode有助于解决问题,我会告诉你的。 - sarasvati
@RobertWang 很抱歉,我还没有解决这个问题。为了能够对计算管道进行分析,我最终使用了计算管道和渲染管道两者。因此,我以每秒60帧的速度进行渲染,并且在每一帧中,我首先通过计算管道计算数据,然后将数据输入到渲染管道中。当使用渲染管道时,捕获边界会在内部插入,因此无需手动执行。如果您不想使用渲染管道,我建议尝试在循环中运行计算管道(2-5次循环)。例如,在上面的代码中:tick();tick();tick();。(未经测试。) - sarasvati
@RobertWang 好的,经过一年的努力,我终于搞明白了 :) - 请看我的回答。我想你肯定已经知道这个,但我只是想提醒你一下。 - sarasvati
显示剩余4条评论
1个回答

5
在iOS 11中,我们可以使用 MTLCaptureManager 来可靠地捕获甚至一个计算内核的调用。
当您运行下面的示例时,它会开始捕获。您可以通过 Xcode 的 "GPU Capture 按钮" (见图像) 或通过 MTLCaptureManagerstopCapture() 方法以编程方式停止捕获。

GPU Capture button

// 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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接