GPU Reshape
GPU Reshape 是一款强大的工具,它利用 GPU 操作的即时插桩功能,并对潜在未定义行为进行指令级验证。
现代 API 正在迅速变得复杂,每增加一个功能都会带来更多的责任和风险。通常,我们首先会使用标准的验证层来确保编写符合规范的代码。然而,如果问题在 CPU 时间线上无法进行静态验证的动态着色器行为中存在,该怎么办?
问题可能源于 GPU 上的动态着色器行为,而这种行为在 CPU 时间线上无法进行静态验证。如果是这样,这可能导致数小时的调试会话,才能在大量的操作中找到那一个罪魁祸首。潜在问题包括索引越界、多个阶段之间的 NaN 值传播,或初始化丢失导致访问无效数据。
您是否曾希望有一个工具可以帮助您找到这些类型的错误,并在 GPU 上验证动态着色器行为?
隆重推出 GPU Reshape,该工具集利用 GPU 操作的即时插装,并支持 DX12 和 Vulkan 的指令级验证潜在的未定义行为。这是一个独立的桌面应用程序,无需集成,全部开源(MIT),现已提供 Beta 版本。

我叫 Miguel Petersen,是 Striking Distance Studios 的高级渲染工程师,也是 GPU Reshape 的作者。
该工具集是与 AMD 和 Avalanche Studios Group 合作开发的,最初是 Avalanche 的一个概念验证 Vulkan 层,之后在外部继续开发。开发得到了 Lou Kramer、Jonas Gustavsson、Rys Sommefeldt、Mark Simpson、Marek Machliński、Daniel Isheden 和 William Hjelm 的支持。感谢大家。
GPU Reshape 将 CPU 工具中典型的强大功能带到了 GPU 上,提供了对动态行为的验证,例如
所有这些都以交互式帧速率完成。
此外,某些功能(如描述符验证和循环)可以保护潜在的错误操作,防止在插装过程中出现未定义行为。当错误可能导致 GPU 崩溃时,这一点尤其有用,因为它限制了应用程序写出该问题的有用调试信息的能力。
验证错误将报告在源代码的确切行,例如,包括访问的资源、尺寸和坐标。GPU Reshape 与前端语言(如 HLSL 或 GLSL)无关,因为它仅基于指令和相关符号进行操作。

如果符号不可用或不希望使用,验证错误可以报告在错误的指令上。指令流是内部中间语言的指令流,详情请参阅作为框架的插装。

支持 SPIR-V 和 DXIL(计划支持 DXBC)的调试符号,可以是嵌入式的,也可以是通过外部托管的 PDB 文件。GPU Reshape 不需要调试符号即可生成有用的信息,但符号可以极大地提高工具追踪问题的能力。
开箱即用,无需集成,只需点击几下即可完成。可以在应用程序启动后进行附加,或者从工具集中启动具有所需工作空间的应用程序。工作空间代表图形(API)设备连接,并包含所有插装状态、着色器、管道和验证数据。
连接到现有应用程序是一项可选功能,可大大提高易用性。此外,如果配置得当,GPU Reshape 可以跨网络边界连接到正在运行的应用程序,从而允许开发人员在发生损坏时对例如艺术家的机器进行插装。这与花费数小时重现问题形成对比。

在指定工作空间启动应用程序后,将立即进行插装。可以通过双击任何验证错误来对其进行更详细的检查。

可以即时更改插装,并且可以针对每个着色器和管道进行专门化,工作空间可以按任何方式配置,没有限制。如果应用程序是在启动后连接的,这是一种常见模式。
该工具集旨在为您提供尽可能多的信息来调查和解决故障,并在几秒钟内实现应用程序的交互式插装。然而,在底层,GPU Reshape 不仅仅是一个固定的工具集。
GPU Reshape 的核心是一个模块化的、API 无关的插装框架。它在 CPU 端执行适当的调用挂钩、着色器代码插装以及任何额外的状态管理。
着色器插装在通用的基于 SSA 的中间语言上进行。它是一种为 GPU Reshape 定制编写的自定义中间语言,并且可以双向翻译到后端语言——即 SPIR-V 和 DXIL(DXBC 实验性)。每个功能,例如越界读/写验证,仅操作于中间语言,对后端语言或 API 均不可见。
// Emitters take care of creating instructionsIL::Emitter emitter(program, context.basicBlock);
// any(coordinates > buffer.GetDimensions())IL::ID failureCondition = emitter.Any(emitter.GreaterThanEqual( loadBuffer->index, emitter.ResourceSize(loadBuffer->buffer)));
// Branch to error block if the condition failed, otherwise resume blockemitter.BranchConditional( failureCondition, errorBlock, resumeBlock, IL::ControlFlow::Selection(resumeBlock));通过自定义中间语言将功能与后端解耦具有许多优点。它可以减少排列组合,因为功能不需要为每个后端实现,并且引入后端不需要更改功能。随着功能和后端数量的增长,这一点变得至关重要。中间语言还允许跨后端使用标准化的工具集,从而显著降低了编写插装代码的复杂性。此外,双向翻译是单层的,这意味着它直接与后端二进制文件进行翻译,而无需其他中间语言。这大大提高了翻译速度。典型的着色器插装只需几毫秒,尽管这取决于着色器的复杂性。
每个功能都可以根据需要修改程序,例如添加、删除和修改指令。功能被赋予一个着色器“程序”,它充当活动后端的可抽象表示,用户可以从中访问所有函数、指令、常量、类型等,并可以根据需要进行修改。修改后,后端会将修改后的程序即时重新编译回后端语言。
功能无需关心后端细节,例如向量化与标量化执行、控制流差异以及其他实现细节。在合规性的前提下,每个功能都将无缝翻译到后端语言。

GPU Reshape 旨在作为一个插装框架,作为一个模块化的基础,可以在此基础上实现任意数量的工具、技术和优化。与标准的验证层一起,用于验证静态已知行为,GPU Reshape 作为一个完全互补的工具集,覆盖了 GPU 上的动态行为。
我希望随着时间的推移,它能变得成熟和发展成为一个通用工具。事实上,有一些潜在的计划添加,例如
加入我们在 Github。我们非常欢迎您的协作、讨论和错误报告!
支持的 API
支持的 IR
所需驱动
所需软件
支持的操作系统
Linux® 支持是计划中的一项添加。
*1 - 在大型应用程序中,尤其是在存在别名时,可能会出现一些误报。将得到修复。
*2 - 实验性,内部转换为 DXIL。正在考虑原生支持。
Avalanche Studios Group 和 Avalanche Studios Group Logo 是 Avalanche Studios Group 的商标。