使用 Windows® 性能分析器分析视频内存

首次发布:
Cristian Cutocheras's avatar
Cristian Cutocheras

为成功发布 Windows 游戏,对视频内存使用进行预算、测量和调试至关重要。开发人员可以借助 Microsoft® 的 Windows® 性能分析器 (WPA) 工具以及对操作系统如何管理视频资源的总体理解,高效地实现这一目标。

安装 WPA

Windows 性能分析器是 Windows 性能工具包的一部分,而 Windows 性能工具包又是 Windows 10 SDK 的一部分。安装 Windows 10 SDK 时,请确保选中“Windows Performance Toolkit”复选框。安装的典型文件路径是 C_:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit\_。在该文件夹中有一个需要编辑的 `perfcore.ini` 文件,以启用视频 GPU 段使用率选项卡,这是进行视频内存分析所必需的。`Perfcore.ini` 包含一个 `.dll` 文件列表,其中需要添加 `perf_dx.dll`。

捕获 .etl 分析文件

默认情况下,安装包中包含一个用于触发捕获的 `log.cmd` 脚本。启动后,将捕获整个机器的活动,因此包含所有进程。运行此脚本需要管理员权限。作为示例,典型步骤如下:

  1. 以管理员身份打开命令提示符。一种简单的方法是在 Windows 搜索框中输入 `cmd`,当出现“命令提示符”菜单选项时,右键单击它并选择“以管理员身份运行”。
  2. 运行目标应用程序并设置感兴趣的设置,然后准备测试用例。
  3. 如果计算机没有多个显示器且应用程序全屏运行,可以使用 `Alt-Tab` 切换到命令提示符后再切回。通过输入 `cmd` 的路径(很可能是 C_:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit\gpuview\log.cmd_)从命令提示符运行它。
  4. 一旦 `cmd` 运行完毕(分析已开始),运行应用程序执行预期测试用例。如果测试游戏中的流式资源,请使用最高视频设置,并尽可能快地让玩家穿过高密度世界区域。例如,驾驶一辆快车穿过城市中心区域。如果调查卡顿问题,请重现卡顿。至少应捕获几秒钟,因为启动/停止分析会造成一定的干扰(导致卡顿)。
  5. 通过再次运行 `cmd` 停止分析。分析的时间段不宜过长,除非必要,因为生成的输出会非常大,难以存储以供将来参考,在网络上共享速度慢,并且在分析时难以导航。
  6. 在 `cmd` 所在的同一个 `gpuview` 目录中,将处理输出以生成 `Merged.etl` 文件。
  7. 检查 `etl` 文件的时间戳,确保它是刚刚生成的,而不是上一次分析的残留文件。
  8. 在文件浏览器中查看 `etl` 文件大小时,刷新浏览器几次,确保文件写入已完全停止。
  9. 重命名和/或将文件复制到其他位置进行存储,否则后续分析将覆盖它。

分析会产生大量数据,因此为了应对大小问题,最好将 WPA 安装在 SSD 上(因为输出文件夹也是 SSD)。另外,最好处理小于 1GB 的 `Merged.etl` 文件,方法是限制捕获时间。可以使用 WPA 或 GPUView(或同时使用两者)打开 `Merged.etl` 文件进行分析。

GPU 段使用率

WPA 是与 `.etl` 文件扩展名关联的默认应用程序,因此要打开 `Merged.etl` 捕获文件,只需在文件浏览器中双击它们即可。捕获文件加载完成后,WPA 窗口的左侧应有一个“Graph Explorer”选项卡,其中包含图表类别。如果 `Perfcore.ini` 设置正确,在“Video”下拉菜单下将有一个“GPU Segment Usage”图表。要打开它,双击它,应会打开一个“Analysis”窗口,该窗口可以最大化。

“GPU Segment Usage”图表的默认布局。

图表下方是一个包含视频分配的表。每个表行对应一个视频分配或一个用于分组分配的下拉菜单。每个表列对应一个分配属性。列被分成由彩色表线分隔的区域。

在黄线左侧,是分配分组的属性,分组顺序从左到右。默认情况下,分配首先按“Segment Type”分组,然后按“Adapter”,最后按“Segment Id”。

在蓝线右侧是“size”和“legend color”列。“size”列显示分配行的单个分配大小,以及下拉菜单行中所有分配的总和。“legend”列在实心框中显示给定分组的关联图表颜色。如果仅显示框的轮廓,则表示该特定分组未在图表中绘制。单击“legend”列下的彩色框可切换各个分组的图表可见性。

在黄线和蓝线之间是常规数据列,显示单个分配的属性。这些列不显示分配分组的信息。

默认分组效果不佳,但很容易更改。通常,更有用的分组是先按“Adapter”分组,然后按“Segment Type”,最后按“Process”。这可以通过用鼠标拖动列到正确的位置来完成。可以通过右键单击表头并取消选中相应的框来删除“Segment Type”列。

由于分组易于重新配置,因此在分析时经常进行此操作,以便轻松导航表格。另一个有用的技巧是“Filter To Selection”(过滤到所选内容):例如,如果发现配置文件之外的进程对 GPU 段使用率的贡献微不足道,则可以将分组更改为“Adapter”、“Process”和“Segment ID”,展开所需适配器的“Adapter”选项卡,然后右键单击目标进程并选择“Filter To Selection”。这将隐藏与该特定适配器和进程无关的所有分配。

配置完分组后,通常需要设置“Legend”选项卡以显示有意义的图表。

在此《文明 VI》的捕获中,自定义分组可以列出目标应用程序的所有已驱逐资源。已选择单个分配(蓝色背景行),WPA 会方便地在图表的时间线上突出显示该分配的生命周期。

识别 GPU 段

对于 AMD,有三个 GPU 段:

  • Local visible(本地可见):此内存大小固定为 256MB,与 GPU 无关。“Local”表示该内存是显存 (VRAM),而“visible”表示 CPU 可以直接访问此视频内存。此段中的大多数资源都标记为 CPU_READ/WRITE。
  • Local invisible(本地不可见):大小取决于 ASIC。例如,在 Radeon RX 480 8GB 卡上,大小为 8GB - 256MB = 7.75GB。“Invisible”表示 CPU 无法直接访问此视频内存。应用程序的大部分图形资源应驻留在此处。
  • System(系统):大小相对较大,通常上限约为系统内存的一半。这是 GPU 可以直接访问的系统内存(非显存)。DirectX 11 暂存和动态资源以及 DirectX 12 上传堆资源通常占据大部分使用量。

此外,WPA 在段 ID -1 下列出了已驱逐的资源。资源被驱逐的原因之一是当它们需要驻留在特定段中但这些段中已无空间时。例如,对于某些资源,各种限制可能要求在使用时将其驻留在“Local Visible”中。如果过多的此类资源争夺空间,视频内存管理器将驱逐其中一些以腾出空间。这可能导致抖动模式,因为被驱逐的资源需要移回。在各个段 ID 之间来回复制的资源会表现为应用程序中的卡顿/峰值。

evicted”(已驱逐)段是唯一可以通过段 ID 识别的段,因为它始终是 -1。实际的 GPU 段不容易与段 ID 匹配,因此使用 WPA 分析 GPU 段使用率的第一步始终是确定配置文件中的哪个段 ID 对应哪个 GPU 段。

在特定时间点(尤其是在使用高峰时)特定段上的所有分配的总大小,是在快速识别段时需要获取的重要信息。但是,“Size”列将显示图表中可见的整个时间间隔(默认为整个配置文件的生命周期)的所有分配的总和,因此该信息对于理解峰值容量无用。图表实际上包含了所需信息,因为它绘制了随时间变化的总体分配大小。因此,首先,对于每个段 ID,切换“Legend”列的颜色,以便一次只绘制单个段,并从图表中检索每个段的大小信息。如果某个段的总容量使用量存在很大差异,那么峰值使用量最能帮助识别该段。

在此《文明 VI》加载屏幕的配置文件中,仅通过配置“Legend”列绘制了“Local Invisible”段。随着更多图形资源的加载,段使用量单调增加,因此使用峰值可以在时间轴末端找到。

识别段的粗略步骤如下:

  • Local invisible(本地不可见)通常会容纳并应容纳大部分资源,因此峰值使用率最高的段最可能是Local invisible
  • Local visible(本地可见)的峰值使用率不能超过 256MB,因此如果两个段的峰值使用率都超过此值,则排除后第三个段必须是Local visible
  • 如果system(系统)段小于 256MB(这是常见情况),您可以检查两个未识别段中的一个的峰值使用率是否接近 256MB 的上限。如果是这样,则切换“Legend”列以仅显示该段和“Evicted (-1)”(已驱逐),并注意这两个段的大小变化是否相关。如果是,则已识别出Local visible和过载问题。
  • 如果Local visible段未出现过载,这意味着大部分 CPU 可见标记的资源将最终驻留在其中。在“Flags”(标志)列下,检查两个低使用率(<256MB)段中的分配,并查找“CpuVisible”。Local visible段通常以标记为 CPU 可见的资源为主,而在“System”段中则很少见。需要注意的是,如果Local visible出现过载,这种情况将不成立,因为通常应驻留在Local visible中的资源可能会移至“System”以避免抖动,因此需要首先遵循前面的步骤。

检查视频内存过载

一旦确定了段,最显而易见的事情就是确定是否存在过载。Local VisibleLocal Invisible中的过载会产生两个负面影响:资源可能最终驻留在次优段中(例如,纹理可能从系统内存而不是本地内存使用,导致性能问题),并且抖动可能导致卡顿和峰值,因为资源在各个段之间来回复制。

要检查由于过载导致的卡顿,请查看“Evicted”(已驱逐)段,并按“Start Time”(开始时间)列对分配进行排序。一些分配在分析开始时可能已经被驱逐,因此表格的前几行应显示 0 作为“Start Time”。所有其他被驱逐的分配都是在分析过程中被驱逐的。评估这些驱逐的大小和频率,以确定它们是否可能成为问题。如果这些驱逐很大或经常发生,请查看其他段,并将“Evicted”段中的“Start Time”与源段中的“End Time”(结束时间)进行匹配,以找出这些资源是从哪里被驱逐出来的。某些资源可能一开始就被驱逐,然后被转移到某个段,这与过载完全无关。这些资源通常在“Evicted”段中停留很短的时间,因此可以通过观察“End Time”和“Start Time”的差值(大约 0.001ms 左右)来识别它们。

要检查资源是否最终驻留在次优段中(例如,首选用于Local Invisible的资源最终驻留在System中),请检查任一本地段的峰值使用率是否接近容量。如果是,对于Local Invisible,确认过载的最简单方法是安装具有比您的应用程序预算更高的 VRAM 的 GPU。例如,如果您担心 4GB Fury X 上的过载问题,请改为在 8GB Radeon RX 480 上进行分析。比较两张卡上“System”和“Local Visible”段的峰值使用率,如果 4GB 卡使用了更多的“System”和//_或_“Local Visible”(并且Local Invisible也接近容量),则意味着 4GB 卡上的Local Invisible过载。如果无法获得更高 VRAM 容量的卡,或者Local Visible接近容量,请比较目标段的图表与其它段,并尝试找出段之间使用率变化的关联(尝试确定目标段中的资源是否被移至其他段或被驱逐)。

最终,检查“System”和“Evicted”段的内容。如果这些段的总使用量很大,这本身就值得关注,因为它暗示着过载、资源泄漏、很少使用的资源或视频内存管理的其他问题。如果很大,则应识别这些段中的资源。

识别视频内存资源和预算

视频内存过载/抖动的常见解决方法是优化应用程序以使用更少的视频内存。要实现这一点,需要能够将视频内存使用量分解为类别进行预算,并且还能够识别 WPA 分配(理解它们与哪些应用程序资源相关联)。

为了识别资源类别,将所有分配导出到电子表格编辑器会很有帮助。要做到这一点,请将所有需要导出的分配分组到 WPA 中的一个下拉菜单下。通常,导出属于某个进程的所有分配很有用,因此在 WPA 中,可以将列配置为只留下“Process”列在黄线左侧。要实际导出,请选择目标进程下的所有分配(Shift+单击),右键单击它们并选择“Copy Selection”(复制所选内容)。然后可以将单元格粘贴到电子表格编辑器中。

要获得资源的良好概览和粗略分类,请创建一个数据透视表,首先按“Height”(高度)分组,然后按“Width”(宽度)分组。让数据透视表计算每个类别中的资源数量,并汇总资源大小。此数据透视表可以按资源大小总和进行排序,以快速显示最重要的资源类别。

此数据透视表按维度对应用程序的所有图形资源进行分类,存储在“Local Invisible”中。在此案例中,最大总使用量是由与屏幕分辨率相同尺寸的资源引起的,如果可能,通过使用少于 6 个屏幕大小的 RT 可以对此进行优化。非 2 的幂次纹理使用率也表明,通过将这些纹理对齐到 2 的幂次大小,可以轻松节省至少 100MB。

很有可能,最相关的类别是:

  • 高度为 0 的资源
    • DirectX 11 子分配:这些是小资源的集合,驱动程序将它们分组到更大的分配中,以避免因对齐限制而浪费 VRAM。子分配的大小通常相同,通常为 32KB(但也可能是其他相似的 2 的幂次大小)。总子分配大小通常很大(占总体 VRAM 使用量的 10-20%)。由于实际资源隐藏在子分配后面,因此很难进一步分析或优化此类。
    • 着色器二进制文件
    • 驱动程序内部资源
    • 描述符堆
    • DirectX 12 放置资源堆
  • 高度为 1 的资源:这些是缓冲区(顶点缓冲区、索引缓冲区、常量缓冲区、1D 纹理等)
  • 纹理(宽度/高度 > 1)
    • 屏幕大小或屏幕尺寸的倍数:这些很可能是所有渲染目标 (RT)。该类别有望与屏幕分辨率成比例缩放。为多个渲染通道重复使用相同的 RT 而不是创建专用 RT 是该段的常见优化。
    • 2 的幂次大小的纹理:这是快速扫描意外情况的简便类别。例如,可以看到异常高的纹理尺寸或纹理数量。
    • 非 2 的幂次大小的纹理:对于带有 mip 贴图的纹理,分配大小需要适应四舍五入到下一个 2 的幂次大小的纹理。如果纹理的一个尺寸略大于 2 的幂次,则会浪费大量 VRAM。例如,一个 530/530 的纹理(带 mip 贴图)将占用与一个 1024/1024 的纹理(带 mip 贴图)相同的空间。因此,对于此类,最好确保总大小不显著,或者纹理尺寸略小于 2 的幂次。此类问题的常见解决方法是调整纹理大小以最接近的 2 的幂次(例如,将 530/530 的纹理大小调整为 512/512)。

为了更好地理解视频内存使用情况,创建额外的数据透视表或使用其他过滤(按段、格式、标志等)很有用。一个有用的例子是创建一个仅包含小型资源的数据透视表。对于 DirectX 11,这些可以是所有小于子分配大小(通常为 32KB)的资源。对于 DirectX 12,可以指定任意大小(或多个大小用于其他数据透视表)。小型资源在 DirectX 11 中总大小应微不足道,因为其中大部分应由驱动程序自动子分配。对于 DirectX 12,由应用程序负责子分配大多数小型资源,因此这是检查分析的分配是否符合预期应用程序行为的好方法。

其他纹理可能会产生填充,非二次幂纹理也可能如此,尤其是在高端显卡上,宽内存总线需要更大的对齐才能实现最大性能——这种额外的填充是典型的独立 GPU 与集成和其他统一内存解决方案之间最大的区别之一,因为后者的总线没有那么宽。要留意分配大小明显大于图像数据的项。电子表格可以设置为计算预期大小并与实际大小进行比较。如果存在大量浪费,减少这种开销的选项包括将小纹理合并到更大的纹理图中,以及调整绑定标志或纹理格式,为驱动程序提供切换到更小填充大小的选项(例如,渲染目标或深度缓冲区比纹理更容易产生填充开销)。纹素尺寸较小的格式通常比纹素尺寸较大的格式需要的填充更少,但 BC 格式除外(4bpp BC 格式等同于 64 位纹素,8bpp BC 格式等同于 128 位纹素)。

WPA 有许多功能和用例,这可能使该工具难以入手。本文提供了一个更易于理解的入门介绍,让读者能够在此基础上进一步自行探索。我希望这能增进对硬件和驱动程序模型的理解,并希望开发人员能充分利用这些知识来提供高性能图形。

Cristian Cutocheras's avatar

Cristian Cutocheras

Cristian Cutocheras 是 AMD 开发者技术部门的成员。第三方网站链接仅为方便之用,除非另有明确说明,AMD 对此类链接网站的内容不承担任何责任,也不暗示认可。

相关新闻和技术文章

相关视频

© . This site is unofficial and not affiliated with AMD.