ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RenderDoc 着色器反射(Shader Reflection)完全指南:API 无关的绑定接口与调试信息解析

RenderDoc 着色器反射(Shader Reflection)完全指南:API 无关的绑定接口与调试信息解析 开发工具调试器图形学GPU【免费下载链接】renderdocRenderDoc is a stand-alone graphics debugging tool.项目地址https://gitcode.com/gh_mirrors/re/renderdoc点击查看免费下载导读本文围绕 RenderDoc 提供的着色器反射数据模型展开讲解如何通过ShaderReflection及其关联结构体ConstantBlock、ShaderSampler、ShaderResource、ShaderDebugInfo等读取着色器期望绑定的接口、常量布局与调试信息。你将掌握 RenderDoc 如何把 D3D11/D3D12/OpenGL/Vulkan 的着色器元数据抽象成统一视图学会用 Python 脚本 API 无关地枚举常量块、采样器、只读/读写资源并利用调试信息判断着色器是否可被调试。本文内容以 shader_refl.rst 为主体辅以 shader_types.h 源码与 shader_refl 示例 佐证。ShaderReflection着色器接口的统一抽象在 RenderDoc 中着色器反射数据通过ShaderReflection暴露它描述了着色器期望被绑定到管线上哪些接口、以及这些接口的格式与内容。数据可用程度高度依赖图形 API以及着色器二进制中保留的元数据或单独提供的调试信息——在某些 API 上信息可能被剥离此时 RenderDoc 只会给出它能得到的最佳数据。这套反射模型是一层抽象一方面尽量让脚本能够与 API 无关地编写另一方面大多数情况下也能直接表达 API 专属概念。文档还特别指出本文不展开“资源如何绑定到着色器”的细节那是 descriptors_bindings.rst 的主题只说明正常绑定路径之外的资源绑定方式。从 shader_types.h 源码可以看到ShaderReflection的完整字段resourceId、entryPoint、stage、debugInfo、encoding、rawBytes、dispatchThreadsDimension计算着色器工作组维度、outputTopology、inputSignature/outputSignature以及四类绑定列表constantBlocks、samplers、readOnlyResources、readWriteResources。此外还包含interfaces基本废弃的接口字符串列表、pointerTypes着色器中引用的指针类型列表以及仅与特定着色器类型相关的taskPayload任务/网格着色器通信载荷布局、rayPayload光追着色器射线载荷和rayAttributes相交/命中着色器属性结构三个常量块布局描述。该结构体的文档注释明确写着“The information in this structure is API agnostic”本结构中的信息与 API 无关。四类绑定接口RenderDoc 的分类法RenderDoc 将所有资源绑定映射为四个宽泛类别。绝大多数资源都能干净地落入某一类熟悉常见 API 概念即可无意外地理解常量块ConstantBlock以纯数值形式绑定、对着色器只读的绑定最常见的是常量/统一缓冲区constant/uniform buffers。不同 API 可能有多种绑定方式但任何“仅用于少量常量数据”的缓冲区都会归入此类。注意那些可能被写入、但被标记为只读或恰好只被读取的绑定不算常量块。采样器ShaderSampler通过 API 绑定机制单独绑定的纯采样器。不包括某些 API 支持的“纹理采样器”组合对象。只读资源ShaderResource显式以只读方式绑定到着色器的其他资源包括纹理、类型转换缓冲区有时称 texture buffer、格式化或结构化缓冲区。读写资源ShaderResource与只读资源类似可以是纹理或缓冲区但着色器既可以读也可以写。标记为“仅写”的资源仍归入此类因为它不是只读资源。常量块与只读缓冲区之间存在重叠每种 API 都有自己的区分方式下文详述。典型区别是只读缓冲区通常承载的数据量远大于常量块可能拥有更高的 API 上限例如允许超过 64KB 数据或设计为容纳大型结构体数组甚至向量数组。各类绑定的通用属性无论哪种绑定类型都有几个含义一致的通用属性见 shader_types.h 中ConstantBlock、ShaderSampler、ShaderResource的公共字段name着色器中变量的名字。bindArraySize对于支持绑定数组的 API给出着色器中声明的数组大小非数组时为 1数组无界时可能是一个非常大的数。fixedBindSetOrSpace与fixedBindNumberAPI 专属的绑定点概念可能相对于类型是绝对或相对的。这两个值仅用于信息展示不用于定位任何绑定或资源。源码注释进一步说明按fixedBindSetOrSpace再按fixedBindNumber排序通常能得到合理的绑定顺序但数字是 API 专属的不保证跨资源唯一只能在能按 API 解释它的上下文中使用。各 API 的映射关系如下APIfixedBindNumberfixedBindSetOrSpaceD3D11绑定寄存器号忽略D3D12寄存器号会在根签名中重映射寄存器的spaceOpenGL不使用绑定可能随运行时 uniform 值动态变化不使用Vulkan描述符集内的绑定号描述符set常量块Constant Blocks详解ConstantBlock描述一类通常为“小而固定结构”的数据布局内部用ShaderConstant列表描述各个变量的布局。最常见的情形是绑定一个简单缓冲区或一段内存区域但也可能是没有显式内存位置的直接值inlineDataBytes甚至是编译期常量compileConstants。块内几个标志位用于区分这些不同形态见 shader_types.hbufferBacked内容是否存储于缓冲区内存中否则通过其他 API 专属方式设置如直接函数调用或编译期特化常量。inlineDataBytes是否由内联数据字节支撑而非特定缓冲区。compileConstants是否为一个列出编译期特化常量的虚拟缓冲区。byteSize块内所有常量占用的总字节数。variables块内常量列表每个ShaderConstant带有name、相对父结构的byteOffset、位域信息bitFieldOffset/bitFieldSize仅整数标量可有位域打包、不超过 64 位时的defaultValue以及typeShaderConstantType描述基础类型、矩阵行列、数组元素数与字节步长等。各 API 的常量块映射D3D11常量块只有常量缓冲区per-stage 绑定到管线bufferBacked恒为True。D3D12常量块只有常量缓冲区可通过根常量root constants、根描述符root descriptors或根表root table绑定bufferBacked在所有常量块上均为True。着色器本身不指定常量缓冲区来自真实缓冲区还是根常量因此反射不提供此信息。通过ResourceDescriptorHeap访问的常量缓冲区不会出现在反射中——截至文档撰写时 DXC 不为这类资源发射任何反射数据无法描述。OpenGL常量块可能是 uniform buffer也可能是一个特殊的虚拟块包含所有“裸”bareuniform。uniform buffer 通过可用绑定点绑定到管线“裸”uniform 则通过glUniform*入口在 program 对象上直接设置。虚拟块的bufferBacked为False以作区分inlineDataBytes也为False因为其存储是不透明的并不真正由可寻址字节支撑。Vulkan有三种常量块形态uniform buffer普通内存支撑表现为bufferBacked True、无其他标志的常量块push constants 区域bufferBacked False、inlineDataBytes True特化常量specialization constantsbufferBacked False、compileConstants True且inlineDataBytes也为True。采样器Samplers各 API 间采样器的差异不大基本直接映射着色器中的采样器对象概念。唯一的例外是OpenGL 没有真正的独立采样器——它只是 API 层面的设置采样状态的便捷机制因此 OpenGL 的 samplers 数组始终为空。D3D12 上通过SamplerDescriptorHeap访问的采样器同样不会出现在反射中DXC 不发射相应反射数据。另外需要注意尽管 API 有“绑定的采样器”与“不可变/静态采样器”之分这发生在着色器外部因此不会列在反射中。只读资源Read-only ResourcesShaderResource结构同时用于只读与读写资源通过isReadOnly区分见 shader_types.h。descriptorType可用于区分单个绑定内的不同类型描述符一般与不同 API 绑定类型 1:1 对应。纹理类绑定textureType给出纹理类型如Texture2D、Texture3DvariableType给出纹理期望的分量类型与数量。缓冲区类绑定variableType给出缓冲区内部变量类型的信息。D3D11 与 D3D12只读资源直接映射到着色器资源视图SRV。所有 SRV 绑定类型都表示为只读资源对 D3D12加速结构acceleration structure不算纹理资源。在 D3D 上StructuredBuffer映射为DescriptorType.BufferBuffer映射为DescriptorType.TypedBuffer后者允许格式转换且元素类型至多列出一个向量类型。缓冲区资源的variableType是结构体数组布局中的结构体。OpenGL只读资源是纹理包括列为DescriptorType.TypedBuffer的缓冲区纹理。缓冲区资源的variableType可能带有一个无界大小的尾部子元素表示缓冲区其余部分是这种类型的数组。Vulkan只读资源包括 sampled images、combined image/samplers、input attachments、texel buffers 和加速结构。加速结构不算纹理资源。input attachment 的isInputAttachment为Truecombined image/sampler 的hasSampler为True缓冲区资源的variableType同样可能有无界尾部数组子元素。读写资源Read-write Resources与只读资源共用ShaderResource结构isReadOnly区分其余成员含义相同不再赘述。D3D11 与 D3D12读写资源映射到无序访问视图UAV。OpenGL读写资源是 SSBO、load/store images 和原子计数器缓冲区。原子计数器缓冲区被当作只含单个无符号整数成员的读写缓冲区变量名为atomic_uint。Vulkan读写资源是 storage images 和 storage buffers。调试信息Debug Information着色器调试信息存储在ShaderReflection.debugInfo类型为ShaderDebugInfo。所有 API 在编译着色器时都能提供额外的调试信息但取决于 API 与编译管线可能需要显式启用或默认被剥离。某些 API 上调试信息可分离到离线文件中——传给图形 API 的字节不直接包含调试信息但包含如何找到它的标识符。编译配置与如何让 RenderDoc 定位这类调试信息见 how_shader_debug_info.rst。诊断问题时值得注意ShaderDebugInfo.debugInfoLoadingLog——它包含调试信息加载过程的日志例如搜索 shader PDB 的过程。如果调试信息不完整RenderDoc 会尽量用可用信息填充其余字段可能留空或信息少于平时与反射信息的处理方式类似。sourceDebugInformation标志指示 RenderDoc 是否已获得足够信息以支持源码级调试通常意味着所有信息都已找到。源码文件Source Code着色器源码文件存储在ShaderDebugInfo.filesShaderSourceFile列表含filename与contents。若调试信息包含一个“仅含单个源文件、通过#line指令引用其他文件”的预处理输出文件则文件列表会同时包含原始预处理输出文件以及按#line指令引用的、虚拟拆分出的部分行文件。这使得即使并非所有文件都存在着色器调试也能引用原始文件中的原始行。文件名的形式取决于具体编译器如何生成调试信息可能是绝对路径、截断路径、含../../的相对路径或仅文件名大小写敏感性也不确定——这些约定全部来自最初生成调试信息的着色器编译器。入口点entry point是某个着色器开始执行的函数——一个着色器反射对象对应一个着色器因此多个不同入口点的着色器可能共享源码。入口点位置在ShaderDebugInfo.entryLocationLineColumnInfo含文件名索引、起始/结束行列号中给出但取决于调试信息并非所有成员都存在。某些 API 上入口点可能在源码名与 API 暴露名之间被重命名此时ShaderDebugInfo.entrySourceName保存源码中的入口点名其余所有使用入口点名称的地方都指 API 面向的名字。编译器与二进制信息Compiler Binary Information着色器可编译为不同编码。例如 D3D12 接受多种着色器编码fxc产生的 DXBC 与dxc产生的 DXIL。即使共享容器格式RenderDoc 也把它们视为不同编码二者大体上截然不同。着色器二进制本身的编码由ShaderReflection.encoding给出着色器源码若不同且已知的编码由ShaderDebugInfo.encoding给出若编译器对应已知着色器工具由ShaderDebugInfo.compilerKnownShaderTool给出未知编译器则置为Unknown编译使用的标志由ShaderDebugInfo.compileFlags给出——一系列键值对有两个特殊已知键cmdline编译器命令行参数字符串spirver仅 SPIR-V 着色器可用包含用于重新编译的目标 SPIR-V 版本如spirv1.3。其他标志可能因着色器编译器、API 与着色器编码而异。在 shader_refl 示例 中可以看到用renderdoc.ToolExecutable(ps.debugInfo.compiler)打印编译器可执行名、用str(ps.debugInfo.encoding)与str(ps.encoding)打印“源码编码 → 二进制编码”示例输出为ShaderEncoding.GLSL was compiled to ShaderEncoding.SPIRV。判断着色器是否可调试Debugging如果要在 history_debug 示例 中进行着色器调试需要先检查着色器是否支持调试ShaderDebugInfo.debuggable单一标志指示此着色器能否被调试。即使 API 总体上支持调试某些使用不受支持功能的特定着色器也可能无法调试。若不可调试原因通常是着色器中使用了不受支持的特性或能力具体说明在字符串ShaderDebugInfo.debugStatus中。实战用 Python 脚本读取着色器反射在 RenderDoc 的 Python 脚本窗口或通过 qrenderdoc 扩展中可像 shader_refl.py 一样获取当前管线状态下的着色器反射。核心入口是CurPipelineState().GetShaderReflection(ShaderStage)若已知特定着色器的 ID 与入口点也可通过ReplayController.GetShader直接查询。关键实操要点输入/输出签名vs.inputSignature/vs.outputSignature中的元素SigParameter可用varNameD3D 等 API 无反射变量名时回退到semanticIdxName、varType、compCount、regIndex读取。特殊内置元素ShaderBuiltin如VertexIndex、InstanceIndex的含义可能随 API 不同而解释不同如从 0 索引或受firstVertex/vertexOffset偏移RenderDoc 只标识、不规定解释。顶点输入的固定功能数据来源可通过PipeState.GetVertexInputs按regIndex查询顶点输出的布局信息可用于解码网格输出数据见 mesh_output 示例。常量块通过constantBlocks[0]读取名称、byteSize、绑定点fixedBindSetOrSpace:fixedBindNumber并用compileConstants/bufferBacked区分三种形态编译期常量 / 运行时非缓冲区临时数据 / 来自缓冲区。块内variables是递归的结构、数组、标量/向量/矩阵描述。若要解码常量块中的变量内容推荐ReplayController.GetCBufferVariableContents——它会处理变量解释细节并内联给出所有值也能处理常量块不来自缓冲区、内容不可直接获取的情况。纹理与采样器readOnlyResources给出期望的资源类型textureType与格式variableType组合 imagesampler 资源没有独立 sampler 绑定而是hasSampler True。反汇编着色器反汇编不在反射中生成与存储反汇编开销较大默认不生成。通过ReplayController.DisassembleShader获取需要传入反射对象部分反汇编格式还期望管线 ID省略可能导致失败或结果略有差异。传空字符串格式时使用 RenderDoc 默认反汇编DXBC/DXIL/SPIR-V 中最易读的形式可用ReplayController.GetDisassemblyTargets(withPipeline)枚举其他格式——注意withPipelineTrue会包含必须管线对象的格式。示例脚本在 VS/PS 上打印签名、常量块与资源的实际运行输出见 shader_refl.rst 的 Sample Output 小节例如VS has 1 constant blocks declared First is named ubuf at 0:0 (from a buffer, expected 1216 bytes) containing 3 variables the first is named MVP at offset 0 type VarType.Float dimension 4x4总结与使用建议ShaderReflection是 RenderDoc 为跨 API 着色器内省提供的统一入口常量块、采样器、只读/读写资源四大类别覆盖了主流 API 的绑定概念fixedBindSetOrSpace/fixedBindNumber保留了 API 专属绑定信息以便展示与排序ShaderDebugInfo则在编译与调试管线之间架起桥梁把源码文件、编译标志、编码与可调试性等信息集中呈现。编写脚本时请始终牢记反射数据的完整度取决于 API 与编译器对可能缺失的字段文件名形式、entryLocation、compileFlags等要做防御性处理并在需要源码级调试时优先检查debuggable与sourceDebugInformation标志。赞分享开发工具调试器图形学GPU【免费下载链接】renderdocRenderDoc is a stand-alone graphics debugging tool.项目地址https://gitcode.com/gh_mirrors/re/renderdoc点击查看免费下载相关推荐RenderDoc Python API 实战着色器反射Shader Reflection解析与调试信息提取RenderDoc Python API 实战着色器反射Shader Reflection解析与调试信息提取 RenderDoc 通过 ShaderRef开发工具调试器图形学GPU一键下载、安装、激活 Office新手 3 步部署指南一键下载、安装、激活 Office新手 3 步部署指南 LKY Office Tools 是一款开源工具一条命令自动完成最新版 Microsoft Offi开发工具调试器图形学GPURenderDoc 着色器调试信息Shader Debug Information完整指南搜索路径配置与手动关联分离调试信息RenderDoc 着色器调试信息Shader Debug Information完整指南搜索路径配置与手动关联分离调试信息 本指南聚焦 RenderDo开发工具调试器图形学GPU创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表