GPUView:从 ETW trace 看 CPU/GPU 排队与掉帧
GPUView:从 ETW trace 看 CPU/GPU 排队与掉帧
GPUView 是 Windows Performance Toolkit 里的工具,基于 ETW(Event Tracing for Windows)抓取系统级 trace,把 CPU 队列、GPU 硬件队列、各进程的 DMA 包、present、vsync 全画在一条时间轴上。它最擅长回答两个问题:这帧是 CPU 还是 GPU 在拖后腿,以及掉帧到底卡在哪个环节。
入门可以先读这篇经典文章 https://graphics.stanford.edu/~mdfisher/GPUView.html,或下面两页速览图:


抓取数据
GPUView 随 Windows SDK / WPT 一起装。用自带的 log.cmd 开始/停止抓取,会生成一个 .etl,再用 GPUView 打开:

定位 swap 事件和掉帧
profiling 一个游戏,第一步是先找到掉帧发生的位置。在 GPUView 里这么做:
- 按
Ctrl + E调出 Event List 窗口 - 选中 DXGI Present 事件
- 点左侧 Select All

之后时间轴上会标出:
- 红色竖线是你代码里调的 present 事件
- 蓝色竖线是 vsync
- 对照 Flip Queue 里的 Flip Events,凡是某个 vsync 前没有对应 Flip Queue Entry 的,就是错过了一次 vsync——也就是掉帧
- 选中卡顿的那一帧,右下角状态栏会显示它的帧时间

这样就能快速框出掉帧位置。接下来判断瓶颈在 CPU 还是 GPU。
判断 GPU bound
看 GPU 硬件队列(Hardware Queue)那一行:
- 如果 GPU 队列始终排满、几乎没有空隙,DMA 包一个接一个、present 在等 GPU 把活干完,就是 GPU bound——GPU 是瓶颈,CPU 提交完命令后在干等。
- 典型特征是 CPU 的 context queue 早早清空,而 GPU 队列还在持续工作,帧的结束时间被 GPU 完成时间决定。
这种情况要往渲染负载上找:draw call 太多、overdraw、shader 太重、带宽打满等。
判断 CPU bound
反过来看 CPU 侧的队列(每个进程的 Context CPU Queue)和 GPU 队列之间的关系:
- 如果 GPU 硬件队列频繁出现空洞(GPU 干完活在等下一个包),而 CPU 队列填包的节奏跟不上,就是 CPU bound——GPU 在饿肚子,瓶颈在 CPU 准备命令的速度。
- 也常见于主线程被某段逻辑卡住、present 迟迟提交不上来,时间轴上表现为 present 红线之间间隔被 CPU 侧的长任务撑大。
这种情况要往 CPU 侧找:游戏线程逻辑、driver 提交开销、command list 录制太慢等,可以再配合 CPU profiler(如 Optick、WPA)细看是哪段代码。
查看调用栈回溯
抓取时如果在 log.cmd 的 trace 配置里打开了带 stackwalk 的 provider,GPUView/WPA 里就能对某个事件展开 CPU 调用栈,看清是哪条代码路径提交了这个 DMA 包或发起的 present。没有栈信息时,事件只能定位到 API 层,加上 stackwalk 才能落到具体函数。
添加自定义事件标记
想在时间轴上插自己的标记,便于把引擎里的逻辑阶段对应到 GPUView 的时间线,可以参考:
注册自定义 ETW provider 后,记得在 log.cmd 的 trace 列表里把你的事件加进去,否则抓不到。