Graphics & Games 高级 25m
使用 GPU Counters 优化 Metal 应用和游戏
Optimize Metal apps and games with GPU counters
2020年6月25日
一句话判断
如果你的 Metal 应用性能不达标,但你看不出瓶颈在哪里,GPU Counters 就是你的性能分析显微镜——它告诉你 GPU 的每一个时钟周期都花在了哪里。
这场 Session 讲了什么
这场 Session 深入讲解了如何使用 GPU Counters 来分析和优化 Metal 应用的性能。GPU Counters 是 GPU 硬件级别的性能计数器,它们可以精确地告诉你 GPU 在每个 render pass 和 compute pass 中的资源利用情况。
演讲者首先介绍了 GPU Counters 的分类体系。最关键的几组 Counter 包括:Vertex Processing(顶点处理耗时)、Fragment Processing(片段处理耗时)、Memory Bandwidth(内存带宽消耗)、和 Shader Core Utilization(着色器核心利用率)。每个 Counter 都有对应的”极限值”——Apple GPU 在特定频率下的理论峰值性能。你的目标就是让实际利用率尽可能接近理论峰值。
Session 的中间部分通过三个真实案例演示了如何用 GPU Counters 定位性能瓶颈:一个受限于带宽的后处理效果、一个受限于顶点处理的粒子系统、以及一个受限于片段处理的复杂着色器。每个案例都展示了从”性能差”到”性能好”的优化路径。
最后,演讲者介绍了如何在 Xcode 12 中可视化 GPU Counters,以及如何在代码中使用 MTLCaptureScope 和 MTLCounterSampleBuffer 进行自动化性能采集。
值得深挖的点
- Counter 的归一化解读:单独看一个 Counter 的绝对值没有意义——你需要将它与峰值性能比较。比如,如果你的 Fragment Processing Counter 显示 40% 利用率,而 Memory Bandwidth 是 95%,那么瓶颈很明显不在着色器计算上,而在内存访问上。优化着色器逻辑不会有帮助,你需要优化数据布局和访问模式。
- Tile-Based 架构的特殊 Counter:Apple GPU 的 TBDR 架构有一些独特的 Counter,如 Tile Load/Store 的带宽和 HSR(Hidden Surface Removal)的效率。这些 Counter 帮助你理解渲染管线中”隐藏”的开销。
代码片段
// 配置 GPU Counter 采样
let counterSampleBufferDescriptor = MTLCounterSampleBufferDescriptor()
counterSampleBufferDescriptor.counterSet = counterSet // 选择 Counter 集
counterSampleBufferDescriptor.storageMode = .shared
counterSampleBufferDescriptor.sampleCount = 100
let counterSampleBuffer = try device.makeCounterSampleBuffer(
descriptor: counterSampleBufferDescriptor
)
// 在 Render Command Encoder 中插入 Counter 采样点
let renderEncoder = commandBuffer.makeRenderCommandEncoder(
descriptor: renderPassDescriptor
)
// ... 渲染命令 ...
// 在特定点采样 Counter
renderEncoder.sampleCounters(
in: counterSampleBuffer,
at: sampleIndex
)
renderEncoder.endEncoding()
// 读取 Counter 采样结果
func resolveCounterSamples(
_ buffer: MTLCounterSampleBuffer,
range: Range<Int>
) {
let data = buffer.resolveCounterRange(range)
// 解析 Counter 数据
data.withUnsafeBytes { rawBuffer in
let samples = rawBuffer.bindMemory(to: MTLCounterResultTimestamp.self)
for sample in samples {
let startTime = sample.timestamp
// 计算耗时等指标
}
}
}
// 使用 Instruments 进行 GPU 分析
// 命令行启动 GPU 性能分析:
// xcrun xctrace record \
// --template 'GPU Performance' \
// --launch -- /path/to/app
// 或在 Xcode 中:
// Product > Profile > GPU Performance
// 查看 Counter 的实时曲线和火焰图
最佳实践
- 先用 Xcode 的 GPU Profiler 获取全局 Counter 概览,再针对高消耗的 pass 进行深度分析
- 关注 Counter 的利用率百分比而非绝对值——50% 以下的利用率意味着有很大优化空间
- 带宽受限的场景优先考虑纹理压缩(ASTC)和 memoryless render target
- 顶点处理受限的场景考虑使用 mesh shader 或简化几何体
- 片段处理受限的场景检查是否存在过度绘制,利用 HSR 优化
还有什么值得关注
- Xcode 12 的 GPU Profiler 界面有显著改进,Counter 的可视化更直观了
- 搭配 “Gain insights into your Metal app with Xcode 12” 一起看,可以掌握完整的 Metal 调试工具链
- GPU Counters 的数据也可以导出为.trace 文件,供离线分析或 CI 集成
WWDC 2020