Shader 调试前提条件

Debug 信息要求

RenderDoc 的 Shader 调试依赖 Shader 反射信息(Reflection Info)和可选的源码级 Debug 信息。反射信息通常默认随 Shader 编译产物一同存在,除非在编译或打包流程中被显式裁剪掉。源码级 Debug 信息(变量名、局部变量映射、函数调用栈等)则通常不会默认内嵌,需要手动启用。

D3D11 / D3D12

  • 编译时向 fxc 传递 /Zi 标志,或向 D3DCompile() 传递 D3DCOMPILE_DEBUG 标志,可将 Debug 信息嵌入 Shader blob。
  • 避免使用 /Qstrip_debug/Qstrip_reflection,这两个选项会裁掉 Constant Buffer 变量名等对调试有用的反射信息。
  • 建议同时使用 /Od(或 D3DCOMPILE_SKIP_OPTIMIZATION)禁用优化,这样可自动启用 HLSL 源码级调试。
  • D3D12 支持通过 /Fd 将 Debug 信息分离到独立文件,RenderDoc 完整支持该方式;详见”Shader Debug Info 配置”章节。
  • Callstack 和局部变量映射信息需要 Windows 8.0 及之后版本的 fxc(对应 D3DCompiler_47.dll)。

Vulkan

  • RenderDoc 通过 NonSemantic.Shader.DebugInfo.100 扩展指令集实现源码级调试。
  • 使用 glslang 时加 -gVS 选项生成 Debug 信息;使用 dxc 时加 -fspv-debug=vulkan-with-source。两者都会将必要的 Debug 信息嵌入 SPIR-V blob。

OpenGL

  • OpenGL 不存在独立的 Debug 信息,RenderDoc 直接使用上传到 API 的 GLSL 源码。若源码已被裁剪或混淆,需在应用层自行处理。

API 支持情况

Shader 调试目前仅支持以下 API:

API支持状态
D3D11支持
D3D12支持
Vulkan支持
OpenGL不支持调试,仅显示源码
其他 API不支持,相关选项隐藏或禁用

部分 Shader 即使在支持的 API 上也无法调试,例如使用了不受支持的 Vulkan 扩展时,相关调试选项同样会被禁用。


启动 Shader 调试器

从 Pipeline State 面板发起

在 Pipeline State 面板中找到需要调试的 Shader Stage(例如 Vertex Shader、Pixel Shader),点击该 Stage 旁边的调试入口,即可进入 Shader 调试器。

调试 Vertex Shader

  1. 打开 Mesh Viewer。
  2. 在 Vertex Input 的 Mesh 数据中选中目标顶点,该顶点及其所属图元会在 Mesh 显示中高亮(需处于 vertex input 模式)。
  3. 右键点击,选择 “Debug Vertex”。
  4. 调试器打开后,Vertex Shader 的输入数据已从 Mesh 数据自动填充。

注意:Geometry Shader 和 Tessellation Shader 暂不支持调试。

调试 Pixel Shader

  1. 在 Texture Viewer 中选中目标像素(详见像素检视文档)。
  2. 在像素上下文区域点击 History 按钮,打开 Pixel History 窗口,查看对该像素的所有修改记录。
  3. 在 Pixel History 窗口中选择要调试的图元,发起调试;Pixel Shader 的输入数据将自动填充。
  4. 若当前选中的 DrawCall 没有写入目标像素,Pixel History 窗口会自动打开供选择;若同一像素被多次绘制(overdraw),默认调试最后一个通过深度测试的 Fragment,可在 Pixel History 中指定特定的 Fragment。

调试 Compute Shader

在 Pipeline State 中的 Compute Shader 区域,输入目标线程的 Group ID 和 Thread ID,即可单独调试该线程。调试时该线程以完全隔离的方式运行,不与其他线程同步。

注意:此功能处于高度实验性阶段,仅对简单 Compute Shader 有效,不提供稳定性保证。


逐步调试操作

Step Into / Step Over

Shader 调试器提供基本的步进控制,界面顶部工具栏包含以下操作:

操作快捷键说明
Run ForwardF5从当前位置运行到程序末尾
Run BackwardShift+F5从当前位置反向运行到程序起始
Step ForwardF10执行当前指令并跳到下一条,遵循流控制(跳转、循环等)
Step BackwardShift+F10跳回到产生当前指令的上一条指令(非简单上一行,可能是跳转源)
Run to CursorCtrl+F10运行到光标所在行后暂停,或运行到 Shader 结束
Run to Sample-运行到下一次纹理 load / gather / sample 操作
Run to NaN/Inf-运行到下一个产生 NaN 或 Infinity 的操作

当前高亮的指令表示下一条将要执行的指令,而非刚刚执行完的指令。

在 HLSL 源码调试模式下,步进操作可能一次跨越多条汇编指令。

断点

F9 可在当前行设置或删除断点。在 Run / Run to Cursor 等运行模式下,执行到断点时会自动暂停。

变量监视

调试器提供三种变量视图:

Constants 窗口

显示在整个 Shader 执行过程中不可变的寄存器,包括:

  • 输入寄存器(Vertex 属性、插值输入等)
  • Constant Buffer 寄存器
  • 资源和采样器寄存器(含基本格式信息)

Registers 窗口

显示可变寄存器,包括:

  • Temporary 寄存器(随执行步进而更新)
  • Output 寄存器

寄存器默认以 float 解释,工具栏提供切换按钮可将其解释为 int。

Watch 窗口

支持自定义监视表达式,可输入涉及输入、Temporary 或 Output 寄存器的任意表达式,并配合 Swizzle 和类型转换:

  • Swizzle 语法遵循 HLSL/GLSL 规则:.[xyzw].[rgba] 的任意组合与重复。
  • 类型转换后缀:,u(无符号整数)、,i(有符号整数)、,f(浮点)、,x(十六进制)、,o(八进制)、,b(二进制)、,c(颜色,显示 RGB 色块)。
  • 存在 Debug 信息时,还可在 Watch 窗口中引用局部变量名。

将鼠标悬停在反汇编或视图窗口中的寄存器上,会弹出 Tooltip 显示多种格式的值解释。

HLSL Callstack 与 Locals

当 HLSL Debug 信息可用时,Callstack 窗口显示当前指令的函数调用栈,Locals 窗口显示当前作用域内所有局部变量的名称和值。


Shader 编辑与热替换(Edit Shader)

场景 Shader 热替换

RenderDoc 允许在不重新采集帧的情况下,直接编辑 Capture 中使用的 Shader 并实时查看效果。

操作步骤:

  1. 在 Pipeline State 面板中找到目标 Shader Stage。
  2. 点击该 Stage 旁边的编辑按钮(铅笔图标)。
  3. 若存在多个编辑选项,会弹出下拉菜单(由 Shader 处理工具决定,见下文)。
  4. 编辑窗口打开后,在编辑器中修改 Shader 源码。
  5. 点击工具栏中的 “Apply changes” 按钮或按 F5 编译并应用修改。
  6. 编译警告和错误会显示在主编辑区下方的 Errors 面板中。

注意事项:

  • 修改会影响所有使用该 Shader 的 DrawCall,不限于当前选中的 DrawCall。
  • 修改在编辑窗口关闭之前一直生效。
  • 若编译出错,Shader 自动回退到 Capture 中原始的 Shader,直到错误被修复。

Shader 处理工具(Shader Processing Tools)

各图形 API 有其原生 Shader 格式:

  • D3D11 / D3D12:DXBC 字节码(D3D12 还支持 DXIL)
  • Vulkan:SPIR-V 字节码
  • OpenGL:GLSL 源码文本

字节码格式的 Shader 可能内嵌了原始源码的 Debug 信息。

编辑 Shader 时,RenderDoc 优先显示原始源码(如果可用),否则会尝试调用已配置的 Shader 处理工具(反编译器)将字节码转换为可编辑的形式。多个处理工具可在设置窗口中配置,RenderDoc 默认内置了若干 SPIR-V 处理工具。若无可用工具,RenderDoc 会生成存根代码或默认的反汇编作为起点。

每个处理工具的输入/输出格式明确定义,例如:编译器可接受 HLSL 并输出 DXBC,反编译器可接受 SPIR-V 并输出 HLSL。

自定义可视化 Shader

自定义可视化 Shader 存储为文件(Windows:%APPDATA%/qrenderdoc/,其他平台:~/.local/share/qrenderdoc)。在 RenderDoc 内点击编辑按钮会打开内置编辑器,保存后实时生效。也可用外部编辑器直接编辑文件,RenderDoc 会自动重新加载,但外部编辑时无法看到编译警告或错误。


Shader Debug Info 配置

嵌入式 Debug 信息

若 Debug 信息已内嵌在 Shader blob 中(未被裁剪),RenderDoc 会自动找到并使用,无需额外配置。若使用编译器提供的机制(如 D3D12 的 /Fd)生成了独立的 Debug 信息文件,只需在 RenderDoc 设置中指定搜索路径,同样无需手动关联。

分离式 Debug 信息搜索路径

当 Debug 信息被手动分离时,需在 RenderDoc 设置窗口的 Core 类别下配置搜索目录。RenderDoc 会按列表顺序搜索各目录。

路径匹配规则:

  • 绝对路径:直接使用,每次均按该路径查找。
  • 相对路径:在配置的搜索目录中逐一匹配。若全部目录均未命中,则移除路径的第一段子目录后重试,以支持绝对路径与搜索目录的尾部子路径匹配。

SPIR-V Debug 信息(Vulkan)

通过 NonSemantic.Shader.DebugInfo.100 扩展指令集将 Debug 信息嵌入 SPIR-V:

# glslang
glslang -gVS input.vert -o output.spv
 
# dxc
dxc -fspv-debug=vulkan-with-source input.hlsl -spirv -o output.spv

DXBC / DXIL Debug 信息(D3D11 / D3D12)

嵌入方式(D3D11 / D3D12):

# fxc
fxc /Zi /Od shader.hlsl /T vs_5_0 /E main /Fo shader.dxbc
 
# dxc (D3D12 DXIL)
dxc -Zi -Od shader.hlsl -T vs_6_0 -E main -Fo shader.dxil

分离方式(D3D12,使用 /Fd):

# 指定文件路径:绝对路径存入 blob
fxc /Zi /Fd path/to/debug.pdb shader.hlsl /T vs_5_0 /E main /Fo shader.dxbc
 
# 指定目录:以 hash 命名存入该目录,blob 中记录相对路径
fxc /Zi /Fd debug_dir\ shader.hlsl /T vs_5_0 /E main /Fo shader.dxbc

运行时指定路径(D3D11 API):

std::string pathName = "path/to/saved/blob.dxbc"; // UTF-8 编码
 
ID3D11VertexShader *shader = ...;
 
GUID RENDERDOC_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
 
shader->SetPrivateData(RENDERDOC_ShaderDebugMagicValue,
                       (UINT)pathName.length(), pathName.c_str());

运行时指定路径(Vulkan API):

std::string pathName = "path/to/saved/blob.spv"; // UTF-8 编码
 
VkShaderModule shaderModule = ...;
 
VkDebugUtilsObjectTagInfoEXT tagInfo = {VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT};
tagInfo.objectType   = VK_OBJECT_TYPE_SHADER_MODULE;
tagInfo.objectHandle = (uint64_t)shaderModule;
tagInfo.tagName      = RENDERDOC_ShaderDebugMagicValue_truncated;
tagInfo.pTag         = pathName.c_str();
tagInfo.tagSize      = pathName.length();
 
vkSetDebugUtilsObjectTagEXT(device, &tagInfo);

注意:分离式 Debug 信息不包含在 Capture 文件中,若文件被移动或删除,RenderDoc 将无法找到,Capture 只能展示无 Debug 信息时的有限内容。


SPIR-V 反汇编查看

在 Vulkan 管线中,RenderDoc 可直接显示 SPIR-V 的反汇编文本。在 Pipeline State 面板中选中 Shader Stage 后,若无源码级 Debug 信息可用,调试器和编辑器均会展示 SPIR-V 反汇编作为默认视图。

在调试器中切换汇编与源码视图

当 HLSL Debug 信息可用时,调试器工具栏会出现 “Debug in HLSL” 按钮;反之在源码调试模式下会出现 “Debug in Assembly” 按钮。也可以通过右键菜单选择 “Go to Disassembly” 或 “Go to Source” 在两种视图之间切换。

源码级调试与汇编级调试使用相同的控制操作(断点、单步、运行等)。

使用 Shader 处理工具反编译 SPIR-V

RenderDoc 默认内置了若干 SPIR-V 处理工具,可将 SPIR-V 字节码反编译为 GLSL 或 HLSL 等可读格式。这些工具在设置窗口中配置,编辑 Vulkan Shader 时 RenderDoc 会自动选择最合适的工具进行处理。

若没有可用工具,RenderDoc 会直接显示 SPIR-V 反汇编文本或生成存根代码作为编辑起点。