iOS Metal:如何正确捕捉GPU帧?
iOS Metal: How to Capture GPU Frame properly?
我正在尝试通过捕获 GPU 帧来分析 Metal 内核。在具有 Metal 运行loop 的应用程序中,我会在调试区域中单击 "camera button",但是我在每个应用程序生命周期中只分派一次内核,因此我无法单击 "camera button"(它保持灰色。
因此,我试图通过在第一次调用 mQueue.insertDebugCaptureBoundary()
之前设置断点和操作 "Capture GPU Frame" 来解决这个问题(参见下面的代码)。
我期望发生的事情类似于 this – 即每个内核函数的执行持续时间概览,其中标明了执行内核函数的各个行所花费的时间百分比。
实际发生的情况是:我很少得到所描述的预期分析概述。大多数时候(大约 95% 的时间)我没有得到这样的分析概述,而是在我构建和 运行 应用程序之后发生以下情况之一:
- 不显示 "Debugging GPU Frame" window – 只有 XCode 的状态栏更改为 "Capturing GPU Frame" 并带有活动的微调器;插图 here.
- "Debugging GPU Frame" window 得到显示,但是没有显示编码命令,因此没有显示执行时间,也没有 GPU objects 浏览器(通过 objects 我的意思是 MTLBuffers 和 MTLTextures);插图 here.
- 一个无标题的 XCode window 弹出说 "timed out (5)" 然后没有其他事情发生;插图 here.
下面的代码显示了我的问题的一个简化示例(如果您想知道;不,我没有 ViewController 中的计算逻辑 - 下面的代码只是一个玩具示例;) ).
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
}
}
在 iOS11 中,我们可以使用 MTLCaptureManager
可靠地捕获计算内核的一次调用。
当您 运行 下面的示例时,它会开始捕获。您可以通过 Xcode 的 "GPU Capture button"(参见图像)或通过 MTLCaptureManager
的 stopCapture()
方法以编程方式停止捕获。
// 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()
我正在尝试通过捕获 GPU 帧来分析 Metal 内核。在具有 Metal 运行loop 的应用程序中,我会在调试区域中单击 "camera button",但是我在每个应用程序生命周期中只分派一次内核,因此我无法单击 "camera button"(它保持灰色。
因此,我试图通过在第一次调用 mQueue.insertDebugCaptureBoundary()
之前设置断点和操作 "Capture GPU Frame" 来解决这个问题(参见下面的代码)。
我期望发生的事情类似于 this – 即每个内核函数的执行持续时间概览,其中标明了执行内核函数的各个行所花费的时间百分比。
实际发生的情况是:我很少得到所描述的预期分析概述。大多数时候(大约 95% 的时间)我没有得到这样的分析概述,而是在我构建和 运行 应用程序之后发生以下情况之一:
- 不显示 "Debugging GPU Frame" window – 只有 XCode 的状态栏更改为 "Capturing GPU Frame" 并带有活动的微调器;插图 here.
- "Debugging GPU Frame" window 得到显示,但是没有显示编码命令,因此没有显示执行时间,也没有 GPU objects 浏览器(通过 objects 我的意思是 MTLBuffers 和 MTLTextures);插图 here.
- 一个无标题的 XCode window 弹出说 "timed out (5)" 然后没有其他事情发生;插图 here.
下面的代码显示了我的问题的一个简化示例(如果您想知道;不,我没有 ViewController 中的计算逻辑 - 下面的代码只是一个玩具示例;) ).
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
}
}
在 iOS11 中,我们可以使用 MTLCaptureManager
可靠地捕获计算内核的一次调用。
当您 运行 下面的示例时,它会开始捕获。您可以通过 Xcode 的 "GPU Capture button"(参见图像)或通过 MTLCaptureManager
的 stopCapture()
方法以编程方式停止捕获。
// 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()