AMD Radeon™ GPU Profiler
AMD RGP 让您能够前所未有地深入了解 GPU。轻松分析图形、异步计算使用情况、事件计时、管线停顿、屏障、瓶颈和其他性能低效之处。
如果你在涉及 AMD GPU 的情况下听说过“上下文回滚”这个术语——我在这篇文章中会经常使用它,提前道歉——那么你可能对这个术语的含义有一个直观的认识。但我敢打赌,你可能忽略了 GPU 和驱动程序方面的一些细节,也许不确定“回滚”到底是什么意思,或者你甚至可能从错误的观念层面去理解上下文。
无论如何,让我来帮你弄清楚,以便你能够明确 AMD GPU 的上下文回滚是什么、它们如何应用于管线以及如何进行管理,以及你可以做些什么来分析它们,找出它们是否是你的游戏或应用程序性能的限制因素。请注意,我在这里写的所有内容都适用于基于 GCN 的现代 AMD Radeon™ GPU。
首先,让我们明确“上下文”的含义。如果你进行过任何 GPU 编程,最常见的上下文是指 API 机制的一个实例,该机制允许你向 GPU 设备发出命令。在这种情况下(我不抱歉),它是一个顶层的软件结构,在游戏或应用程序使用 GPU 时,你通常只持有一个。
然而,这并不是我们在谈论 AMD GPU 上下文回滚时所说的上下文。这里的上下文是指 GPU硬件理解当前需要处于的状态才能正确绘制。你可以调整 GPU 的各种每绘制状态位;例如,前端处理三角形的顺序,或者混合器处于什么模式才能正确地混合值。这些可变的 GPU 状态位适用于整个 GPU 管线。
因此,当我们从上下文回滚的角度谈论上下文时,我们指的是正确绘制某物所需的 GPU 管线状态。
既然我们知道上下文是指 GPU 在绘制期间的操作状态,那么让我们来谈谈一些 GPU 硬件细节。硬件维护 8 个上下文寄存器组来存储操作状态,但 8 这个数字只是一个实现细节。我们为清除状态保留了银行 0,所以有 7 个用于正常使用。但这 7 个可用的银行只是一个逻辑结构;硬件的实际实现要复杂一些。
上下文寄存器的副本确实存储在分块的 RAM 中,但它们分布在整个 GPU 设计中,并存储在需要它们的模块中,而不是全部存储在一个物理 RAM 中。有一个硬件模块负责管理设置新值,它与拥有上下文寄存器的各个模块通信,将任何更新芯片范围地广播给需要知道的模块。
每个单独的模块然后负责管理其副本。至于这个管理模块的驱动方式,则完全取决于命令处理器(CP),而命令处理器由驱动程序驱动。我稍后会对此进行讨论。
现在你知道 GPU 维护着多个上下文寄存器副本,当 CP 要求管理模块开始处理一组新的寄存器,因为需要更改某些管线状态以正确绘制时,就发生了“回滚”。它通过要求每个模块将当前寄存器的一个副本加载到一个空闲的银行中来实现这一点,以便一切都已准备好通过编程新的寄存器值来应用任何新的状态更新。
CP 不会自主运行来管理该状态。相反,它只是按照驱动程序的指示行事。现在写这篇帖子的一个好处是,与 8 个月前我第一次将其添加到列表时相比,我们现在已经开源了我们的 Vulkan 驱动程序,包括 Vulkan 用户模式驱动程序所基于的低级平台抽象层 (PAL)!
驱动程序通过 PAL 负责指示 CP 正确设置 GPU 来处理绘制,它使用一种称为 PM4 的数据包化命令格式来做到这一点。现在,PM4 不再是一个我无法真正谈论的不透明事物,构建和向 CP 发出 PM4 数据包的驱动程序源代码就在那里,如果你想看的话可以查阅。
你不需要阅读它来理解上下文回滚,但如果你好奇,请前往 GitHub 上的 PAL 存储库。例如,查看 GFX9 PM4 操作码,或者在存储库中搜索 EOP(管道结束,我稍后会讲到)或 CmdUtil::BuildLoadContextRegs()。该函数负责构建使用 LOAD_CONTEXT_REG PM4 操作码来编程上下文寄存器的 PM4 数据包。
因此,当你更改任何 GPU 管线状态时,接受这些调用的驱动程序用户模式部分会发出自己的调用到 PAL。然后 PAL 会构建正确的 PM4 数据包和正确的数据范围,所有这些都是为了在正确的时间发生上下文状态更新。假设你从游戏中向 API 发出了 5 个绘制调用,其中 2 个使用一组 GPU 管线状态,另外 3 个使用另一组。理想情况下,你将获得一个具有以下(伪)注释数据包顺序的 PM4 命令流:
IT_SET_CONTEXT_REG; # program some stateIT_DRAW_INDEX_2; # draw 0IT_DRAW_INDEX_2; # draw 1IT_SET_CONTEXT_REG; # program some more stateIT_DRAW_INDEX_2; # draw 2IT_DRAW_INDEX_2; # draw 3IT_DRAW_INDEX_2; # draw 4现在我们知道了 GPU 如何按块管理状态,以及 CP 如何被指示设置它并在之后启动新的绘制,那么性能影响呢?
只要有可用的逻辑上下文寄存器组可以回滚任何新的上下文,就不会产生性能影响。但如果所有 7 个组都已满,并且包含正在进行的绘制所需的必要状态怎么办?如果你的绘制包含足够的工作量,那么仍然应该没有任何影响,因为 GPU 在绘制从 GPU 管线中耗尽、发生相关的管道结束工作时仍然很忙,从而释放了逻辑上下文,因此允许 CP 为新的绘制回滚一个新的上下文。
但是,如果你的绘制包含的工作量不多,你可能会因为 GPU 不太忙而产生的延迟而承担开销,并且没有可用的逻辑上下文来启动新的绘制并重新填充 GPU 工作。在一个编写良好的游戏或应用程序中,这种情况不会发生,但你在开发过程中可能会遇到这种情况。如何发现并改进你的渲染器来解决这个问题?
避免上下文饥渴的最佳方法是明智地向 GPU 提交工作,通常是通过批处理。尽可能批处理并提交具有相同材质和/或渲染状态的绘制,这样后继的绘制理想情况下就不需要回滚到新的上下文。否则,多个上下文寄存器组就有其存在的理由!
CP 知道何时没有免费可用的逻辑上下文寄存器组,并在需要时暂停直到其中一个空闲。但作为开发者,你如何获得通知?我们的 Radeon GPU Profiler 中有上下文回滚分析功能!

RGP 将允许你检查你的帧,它会以多种方式向你展示上下文的状态。概述面板中有明确的通知,此外你还可以为你的绘制着色,以便为使用的每个设备上下文提供不同的颜色。所以,如果时间线看起来像一只独角兽来访,请检查概述面板以找出 CP 是否因为你回滚了太多次而无法开始新的绘制 🌈。
希望这对 GPU 如何在硬件中查看上下文状态、驱动程序如何对其进行编程以及你如何使用 RGP 分析应用程序中发生的情况以避免不必要的重复有了一个不错的概述。
驱动程序会尽力提供帮助,但你对渲染过程中发生的事情有最好的了解,而且我们非常努力地使驱动程序在 CPU 使用率方面保持轻量级,因此将上下文管理的开销留给最有效率的地方进行处理是最好的:你的游戏或应用程序。
AMD GPU Services (AGS) 库使软件开发者能够查询通常无法通过标准操作系统或图形 API 获取的 AMD GPU 软件和硬件状态信息。
Radeon GPU Analyzer 是一款用于 DirectX®、Vulkan®、SPIR-V™、OpenGL® 和 OpenCL™ 的离线编译器和性能分析工具。
RGP 为你提供了前所未有的深入 GPU 访问。轻松分析图形、异步计算使用情况、事件计时、管线延迟、障碍、瓶颈和其他性能低效之处。