RenderDoc 捕获文件访问完全指南:.rdc 容器格式、CaptureFile/CaptureAccess 接口与自定义 Section 扩展
2026/9/24 5:42:18 网站建设 项目流程
  • 开发工具
  • 调试器
  • 图形学
  • GPU

【免费下载链接】renderdoc

RenderDoc is a stand-alone graphics debugging tool.

项目地址:https://gitcode.com/gh_mirrors/re/renderdoc
点击查看免费下载

本篇技术指南围绕 RenderDoc 的捕获文件(.rdc)访问机制展开,系统讲解renderdocPython 模块中CaptureAccessCaptureFile两套接口的定位与用法、打开文件与启动回放的完整流程、文件格式与转换能力,以及通过 Section 读写和 ASCII Section 手工拼接两种方式为捕获附加自定义数据的具体方法。读完本文,你将掌握在不依赖 RenderDoc UI 的情况下,用脚本独立完成捕获文件的打开、元数据查询、格式转换、自定义数据注入与回放启动的完整实战方案。

.rdc 捕获文件是什么

RenderDoc 在应用运行期间触发捕获后,会将一帧(或多帧)的完整 GPU 执行数据保存为.rdc文件。这个文件包含了回放该捕获帧所需的全部数据,同时也包含元数据与附加信息:

  • 捕获使用的图形 API(即源码文档中称为 driver 的部分)及其版本信息;
  • 捕获产生平台的机器标识(machine ident);
  • 帧捕获正文(frame capture)数据;
  • 可选的无损缩略图(extended thumbnail);
  • 可选的调用栈(callstack)及其模块信息;
  • 用户或工具附加的自定义 Section。

从源码层面看,.rdc文件是一个结构简单但组织清晰的容器RDCFile实现位于 rdcfile.cpp,文件以'R','D','O','C'四字符魔数(MAGIC_HEADER,见 rdcfile.cpp#L142)开头,随后是文件头(magic、版本号、版本字符串)、可选的二进制缩略图、捕获元数据(CaptureMetaData,包含machineIdentdriverID)、时间基准(CaptureTimeBase,包含timeBasetimeFreq),最后是一系列紧挨排列的 Section(详见 rdcfile.cpp#L80-L138 中完整格式注释)。

正是由于容器结构简单,.rdc文件可以由脚本管理:既能通过官方 Python API 附加自定义信息,也能用最朴素的方式——把文本格式的 Section 直接拼接到文件末尾(即下文"ASCII Sections"一节)。

这套文件访问能力也正是 RenderDoc 在脱离 UI 情况下启动回放与分析的基础:当你不使用 RenderDoc 图形界面,而是通过 python_module 中描述的 Replay API 完全独立地驱动回放时,首先接触的就是这里讲的捕获文件接口。

访问捕获文件的两大接口:CaptureAccess 与 CaptureFile

捕获文件由两个主要接口管理(见 renderdoc_replay.h#L1243-L1247 的接口声明):

接口定位适用场景
renderdoc.CaptureAccess功能受限的子集可通过网络连接使用,不要求本地磁盘上有该文件
renderdoc.CaptureFile功能更完整必须在本地可访问的文件上初始化,继承自CaptureAccess并追加本地文件能力

具体来说,CaptureAccess支持 Section 的枚举、查找与读写、可用 GPU 列表查询等基础能力(GetSectionCountFindSectionByNameFindSectionByTypeGetSectionPropertiesGetSectionContentsWriteSectionGetAvailableGPUs);而CaptureFile(对应 C++ 侧ICaptureFile,见 renderdoc_replay.h#L1583-L1795)在此基础上增加了:

  • OpenFile/OpenBuffer:从磁盘文件或内存缓冲区初始化;
  • Convert:格式转换与导出;
  • GetCaptureFileFormats:查询支持的格式;
  • LocalReplaySupport:本地回放支持程度;
  • RecordedMachineIdent/TimestampBase/TimestampFrequency:机器与时间信息;
  • GetThumbnail:读取缩略图;
  • GetStructuredData/SetStructuredData:结构化数据读取与注入;
  • OpenCapture:本地启动回放。

注:本文以本地CaptureFile为主线展开,并在需要时标注哪些功能可以通过远程CaptureAccess使用。RenderDoc 的网络回放功能详见 remote_replay。

从零创建一个 CaptureFile

创建CaptureFile的入口是renderdoc.OpenCaptureFile()(对应 C 接口RENDERDOC_OpenCaptureFile,见 renderdoc_replay.h#L1959)。这个句柄归 Python 所有,使用完毕后必须调用CaptureFile.Shutdown()显式销毁:

import renderdoc as rd # 创建句柄(由 python 持有所有权) cap = rd.OpenCaptureFile() # ... 打开文件、读取数据、回放 ... # 使用完毕后显式关闭句柄 cap.Shutdown()

Shutdown()会关闭文件句柄并释放其持有的资源。如果跳过这一步,句柄会一直持有对文件(或内存缓冲)的引用,甚至可能因未释放文件锁而导致后续操作失败。

在 UI 脚本中获取当前捕获

如果你运行在 RenderDoc UI 内的 Python 脚本中,可以直接访问 UI 当前加载的捕获:

  • qrenderdoc.ReplayManager.GetCaptureAccess():获取当前捕获的CaptureAccess(无论本地还是远程打开都可用);
  • qrenderdoc.ReplayManager.GetCaptureFile():获取当前捕获的CaptureFile注意:如果捕获是在远程打开的,此方法会返回None,因为本地并没有该文件。
import qrenderdoc as qrd replay = qrd.ReplayManager.Get() # 任何情况下都可用 access = replay.GetCaptureAccess() # 仅当捕获文件在本地打开时返回 CaptureFile,远程打开时为 None cap = replay.GetCaptureFile()

此外,这两个接口必须仅在 replay thread 上访问(参见 threading 中关于pythreading的说明)。Replay API 的线程模型要求所有回放相关调用在专门的回放线程中执行,UI 脚本应通过qrenderdoc.ReplayManager.BlockInvoke等机制将访问调度到该线程。

打开捕获文件:OpenFile 与进度回调

创建CaptureFile后,调用OpenFile(filename, filetype, progress)打开文件:

import renderdoc as rd cap = rd.OpenCaptureFile() def progress_cb(p): print("Opening... %d%%" % int(p * 100)) result = cap.OpenFile("frame123.rdc", "", progress_cb) if result != rd.ResultCode.Succeeded: print("Failed to open: %s" % result) else: print("Opened successfully")

OpenFile的关键语义(对应 renderdoc_replay.h#L1591-L1610):

  • filename:要打开的.rdc文件路径;
  • filetype:输入格式。绝大多数情况下传""即可——rdc是始终保证受支持的格式,当filetype为空或无法识别时都会按rdc处理;
  • progress:可选进度回调,打开过程中(尤其发生格式导入时)会被间歇调用。回调签名须匹配rd.ProgressCallback(接收一个 0~1 的float)。

另外还有OpenBuffer(buffer, filetype, progress)变体,允许从内存缓冲区初始化句柄,适用于"不想解析整个文件"或"数据已在内存中"的场景(见 renderdoc_replay.h#L1612-L1628)。

打开文件后的磁盘锁

OpenFile成功后,RenderDoc 会对磁盘上的文件持有独占锁,这使得用户无法在文件打开期间复制它。如果需要复制,应使用CopyFileTo(newpath):它会把磁盘文件复制到新位置,新文件加锁、旧文件解锁(以便必要时删除),而原捕获句柄不受影响(见 renderdoc_replay.h#L1630-L1642)。

打开捕获进行回放:OpenCapture 与生命周期约束

拿到CaptureFile后,可以用OpenCapture(opts, progress)启动回放,成功时返回ReplayController(对应 renderdoc_replay.h#L1737-L1755):

import renderdoc as rd cap = rd.OpenCaptureFile() if cap.OpenFile("frame123.rdc", "", None) != rd.ResultCode.Succeeded: raise RuntimeError("open failed") # 使用默认回放选项启动回放,返回 (ResultDetails, ReplayController) status, controller = cap.OpenCapture(rd.ReplayOptions(), None) if status == rd.ResultCode.Succeeded: print("Replay ready, driver: %s" % controller.GetDriverName()) # ---- 在这里进行帧回放、状态查询、着色器调试等分析 ---- # 先关闭控制器,再关闭捕获文件 controller.Shutdown() else: print("Replay failed: %s" % status) cap.Shutdown()

生命周期上有一个必须遵守的顺序CaptureFileReplayController使用期间必须保持打开状态,因此你应该先完成分析并关闭控制器,再关闭捕获文件ReplayOptions用于控制回放方式,例如强制在特定 GPU 上回放(结合CaptureAccess.GetAvailableGPUs()获取的 GPU 列表)等。

注意:OpenCapture仅支持以原生rdc格式打开的句柄,其他格式打开的文件会直接失败(见 renderdoc_replay.h#L1737-L1738)。远程场景下则由IRemoteServer.OpenCapture(proxyid, filename, opts, progress)完成"远程打开、本地代理渲染",且必须用CloseCapture而不是Shutdown来正确清理本地代理(见 renderdoc_replay.h#L1542-L1574)。

捕获文件格式:rdc 与其他格式的转换

绝大多数情况下OpenFile打开的都是标准.rdc文件,此时filetype应传"rdc"或空字符串。RenderDoc 也支持其他格式,但导入支持非常有限——导入要求格式包含回放所需的全部信息;相对而言,导出(Convert)更有实用价值,因为导出可以只包含数据的受限子集。

import renderdoc as rd cap = rd.OpenCaptureFile() cap.OpenFile("frame123.rdc", "", None) # 查询所有支持的格式及其能力 for fmt in cap.GetCaptureFileFormats(): print("ext=%s name=%s desc=%s requiresBuffers=%s open=%s convert=%s" % ( fmt.extension, fmt.name, fmt.description, fmt.requiresBuffers, fmt.openSupported, fmt.convertSupported)) # 将当前文件导出为指定格式 res = cap.Convert("frame123_out.xxx", "xxx", None, None) cap.Shutdown()

GetCaptureFileFormats()返回的CaptureFileFormat(定义见 control_types.h#L1227)字段含义如下:

字段含义
extension格式扩展名,如rdc
name格式名称
description格式描述
requiresBuffers转换该格式是否需要缓冲区数据(与结构化数据导出相关)
openSupported是否支持从该格式导入(打开)
convertSupported是否支持导出到该格式

Convert(filename, filetype, file, progress)还接受一个可选的SDFile参数:当目标格式不需要缓冲区(requiresBuffers == False)且你已经有一个携带结构化数据的ReplayController时,可以传入已有的SDFile避免重新加载文件;传None则内部自行获取结构化数据。

捕获数据:轻量打开与元数据查询

打开文件是一个非常轻量的操作:它只解码容器、加载捕获元数据,不会发起任何图形 API 调用、不会开始回放,也不会把大量数据载入内存。因此适合在脚本中快速对一批捕获做"体检"。

打开后可以查询的元数据包括:

import renderdoc as rd cap = rd.OpenCaptureFile() cap.OpenFile("frame123.rdc", "", None) # 使用的图形 API(driver),如 "D3D11"、"OpenGL"、"Vulkan" print("Driver:", cap.DriverName()) # 本地是否支持回放(返回 rd.ReplaySupport) print("Local replay support:", cap.LocalReplaySupport()) # 记录该捕获的机器标识:平台(x86 / Android 等)与位数(32/64 位) print("Machine ident:", cap.RecordedMachineIdent()) # 缩略图(支持 JPG/PNG/TGA/BMP,maxsize 限制最大宽高) thumb = cap.GetThumbnail(rd.FileType.PNG, 512) print("Thumbnail %dx%d, %d bytes" % (thumb.width, thumb.height, len(thumb.data))) # 时间基准:所有时间戳相对的基础值与频率 print("Timestamp base:", cap.TimestampBase()) print("Timestamp freq:", cap.TimestampFrequency()) cap.Shutdown()
  • DriverName():返回创建该捕获的驱动名称字符串(见 renderdoc_replay.h#L1347-L1352);
  • LocalReplaySupport():查询本地对该捕获的回放支持程度(见 renderdoc_replay.h#L1674-L1682)。如果文件是以非rdc格式打开的,该查询恒返回"不支持回放";
  • RecordedMachineIdent():机器标识字符串,包含平台与位数信息(如 x86 或 Android、32 位或 64 位)。在显示"该捕获不兼容"之类的用户提示时非常有用(见 renderdoc_replay.h#L1684-L1689);
  • GetThumbnail(type, maxsize):返回嵌入缩略图,type仅支持FileType.JPG / PNG / TGA / BMPmaxsize为最大宽高,超出则缩放(见 renderdoc_replay.h#L1779-L1790);
  • TimestampBase()/TimestampFrequency():时间戳基准值与换算频率,时间戳除以频率即可换算为微秒(见 renderdoc_replay.h#L1691-L1705)。

请求结构化数据:SDFile

除了元数据,还可以请求捕获的结构化数据SDFile)。这与回放是完全不同的两个操作:请求结构化数据时,RenderDoc 会解码捕获内的序列化数据,但不会发起任何图形 API 调用。详细机制见 structured_data。

import renderdoc as rd cap = rd.OpenCaptureFile() cap.OpenFile("frame123.rdc", "", None) sd = cap.GetStructuredData() # 返回 rd.SDFile print("Structured data chunk count:", len(sd.chunks)) cap.Shutdown()

关键特性:

  • 可跨平台:只要该构建的 RenderDoc 支持目标 API,就能加载结构化数据。例如 Linux 上因完全没有 D3D 支持而无法加载 D3D 捕获的结构化数据,但 Windows 机器可以加载 Android Vulkan 捕获的结构化数据,即使它无法回放该捕获;
  • 重量级操作:请求结构化数据需要读取并解码整个捕获,比单纯打开文件重得多;
  • 返回的SDFile包含缓冲区(buffers),因此与任何requiresBuffers为真(CaptureFileFormat.requiresBuffers)的导出格式兼容,可直接用于Convert
  • SDFile的生命周期与捕获句柄绑定,句柄销毁后不可再使用(见 renderdoc_replay.h#L1757-L1765)。

反向地,SetStructuredData(sd)允许用生成的SDFile填充捕获(数据会被内部复制),配合SetMetadata(...)可以纯内存地构造一个捕获文件,再通过Convert保存到磁盘(见 renderdoc_replay.h#L1707-L1777)。

捕获 Sections:容器中的信息单元

rdc容器文件由一个小文件头加任意数量的 Section组成。默认情况下至少包含帧捕获本身对应的 Section,通常还包含一个扩展无损缩略图;如果捕获了调用栈,还会有一个平台相关的 Section 记录已加载模块信息,供后续调用栈解析使用。

SectionType:官方预定义 Section

已知的官方 Section 由SectionType枚举定义(见 replay_enums.h#L120-L137),每个枚举值同时对应一个字符串路径(如帧捕获为renderdoc/internal/framecapture):

枚举值说明
Unknown未知/自定义类型,通常为 0
FrameCapture帧捕获正文,路径renderdoc/internal/framecapture
ResolveDatabase调用栈解析数据库
Bookmarks书签
NotesUI 笔记,路径renderdoc/ui/notes
ResourceRenames资源重命名记录
AMDRGPProfileAMD RGP 性能分析数据
ExtendedThumbnail扩展(无损)缩略图
EmbeddedLogfile内嵌日志文件
EditedShaders编辑过的着色器
D3D12Core/D3D12SDKLayersD3D12 相关模块信息
EmbeddedExternalFiles内嵌的外部依赖文件(如着色器调试文件)

Section 的枚举与访问既可以通过CaptureAccess也可以通过CaptureFile完成;两个接口还都支持新增或覆写Section。

读取与写入自定义 Section

自定义数据通过以下 API 读写:

import renderdoc as rd cap = rd.OpenCaptureFile() cap.OpenFile("frame123.rdc", "", None) # 枚举所有 section for i in range(cap.GetSectionCount()): props = cap.GetSectionProperties(i) print("Section %d: name=%s type=%s version=%d flags=%s" % ( i, props.name, props.type, props.version, props.flags)) data = cap.GetSectionContents(i) print(" contents: %d bytes" % len(data)) # 按名字查找(返回索引,找不到为 -1) idx = cap.FindSectionByName("mytool/customdata") # 按类型查找(返回索引,找不到为 -1) idx2 = cap.FindSectionByType(rd.SectionType.Notes) # 写入/覆写一个自定义 section props = rd.SectionProperties() props.name = "mytool/customdata" props.type = rd.SectionType.Unknown props.version = 1 res = cap.WriteSection(props, b"my custom payload") if res != rd.ResultCode.Succeeded: print("WriteSection failed: %s" % res) cap.Shutdown()

接口细节(见 renderdoc_replay.h#L1257-L1309):

  • GetSectionCount():Section 总数;
  • FindSectionByName(name)/FindSectionByType(type):按名字或类型定位索引,找不到返回-1索引不应被缓存,因为写入 Section 可能重排索引顺序;
  • GetSectionProperties(index):返回SectionProperties,描述该 Section;
  • GetSectionContents(index):返回该 Section 的原始字节内容(bytes);
  • WriteSection(props, contents):写入新 Section。若已存在相同类型或名字的 Section,会被覆写(同一捕获中不允许两个 Section 共享相同类型或名字,见 rdcfile.cpp#L110-L111 的格式约束)。

SectionProperties结构(见 data_types.h#L199-L238)包含:

字段含义
nameSection 名字(字符串)
typeSectionType,未知/自定义为Unknown
flagsSectionFlags,描述存储方式(如是否压缩)
versionSection 版本号,含义由类型自行定义
uncompressedSize解压后的数据字节数
compressedSize磁盘上压缩后的字节数

命名规范(重要):自定义 Section 的名字应避免使用renderdoc/前缀。Section 名可以是任意字符串,请使用自己的命名空间(如mytool/...company/project/...),以免与官方内部 Section 冲突。

ASCII Sections:用纯文本手工追加数据

为了给"原始脚本"提供更便捷的访问途径,RenderDoc 支持把文本文件直接拼接到.rdc文件末尾来添加 Section——这样即便不调用 Python API,也能以非常有限的方式向捕获注入数据。

⚠️高级特性警告:这是一个advanced功能,操作时必须非常小心。任何格式错误都可能使捕获文件完全无法打开!除非确实需要这么做,否则请优先考虑更安全的方案(即使用WriteSectionAPI)。

ASCII Section 的格式

一个 ASCII Section 的头部格式如下(示例中带注释,但真实格式必须不含多余空白与注释):

A # 字面字符 'A',表示这是一个 ASCII Section 119 # 内容长度(字节数,十进制数字) 4 # SectionType 的数值(自定义 section 通常为 0) 1 # 本 section 的版本号,用于向后兼容 renderdoc/ui/notes # section 的名字 # 名字之后必须有一个换行符

对照 rdcfile.cpp#L92-L113 的容器格式定义,每个 ASCII Section 的头部逐行含义为:

  1. 一个字面字符A(ASCII 0x41),标识该 Section 为 ASCII 格式,便于脚本/手工直接拼接;
  2. 一个十进制数字字符串,表示紧跟其后的 section 数据长度(字节数)
  3. 一个十进制数字字符串,表示该 Section 的SectionType数值——自定义 Section 通常是0Unknown);
  4. 一个十进制数字字符串,表示 section 版本号;
  5. Section 名字(UTF-8 字符串),后面必须紧跟一个换行符

头部可以用文本编辑器或任何脚本手工构造。如上文所说,第二行的长度(字节数)应该由脚本自动计算生成,而不是手工维护。

头部之后就是 Section 内容,内容可以是任意数据。官方 UI 的 notes Section 是 JSON 格式的,其中"comments"键对应要显示的字符串:

{ "comments": "These are some notes! there isn't really much to put here, except to demonstrate an ASCII section." }

拼接步骤与验证

把上面的头部去掉注释和空白,再拼上 JSON 内容,整体追加到一个已有.rdc文件的末尾:

# 1. 构造 ASCII section 文本文件(头部 + 内容,无注释无多余空白) # 2. 追加到 .rdc 末尾 cat ascii_section.txt >> frame123.rdc

之后用 RenderDoc UI 打开该捕获,即可在 UI 的笔记视图中看到 JSON 中"comments"字段提供的文字内容。这种做法的典型用途就是为捕获批量添加可被 UI 展示的备注信息。

从实现上看,解析器对 ASCII Section 的校验相当严格(见 rdcfile.cpp#L454-L509):

  • 换行序列必须是\n(允许\r\n,单独\r视为非法,返回FileCorrupted);
  • 长度、类型、版本逐行按十进制解析,非法字符会污染解析结果;
  • 解析失败会报ResultCode::FileCorrupted文件将被判定为损坏而无法打开——这正是文档反复警告格式错误后果严重的原因。

二进制 Section 与 ASCII Section 的结构差异

为便于理解容器设计,这里对比两类 Section 的磁盘布局(完整定义见 rdcfile.cpp#L88-L133):

  • ASCII Section'A'标志 + 换行分隔的四行十进制字段(长度/类型/版本/名字)+ 换行 + 原始数据;
  • 二进制 Section'\0'标志 + 3 字节保留位 +uint32类型 +uint64压缩长度 +uint64解压长度 +uint64版本 +uint32flags +uint32名字长度 + UTF-8 名字 + 数据。

二进制 Section 支持压缩(SectionFlags中记录),数据长度在磁盘上和解压后可能不同;ASCII Section 则不支持压缩、布局固定为纯文本,这也是它能被手工拼接的原因。

完整示例:脚本化"打开 → 查询 → 加注 → 回放"流水线

结合以上内容,一个典型的脚本化捕获处理流水线如下:

import renderdoc as rd def handle_capture(path): cap = rd.OpenCaptureFile() try: # 1) 轻量打开,仅解析容器与元数据 if cap.OpenFile(path, "", None) != rd.ResultCode.Succeeded: return None # 2) 元数据查询 print("Driver:", cap.DriverName()) print("Replay support locally:", cap.LocalReplaySupport()) print("Recorded on:", cap.RecordedMachineIdent()) # 3) 写入自定义追踪信息(覆写同名 section) props = rd.SectionProperties() props.name = "mytool/processed" props.type = rd.SectionType.Unknown props.version = 1 cap.WriteSection(props, b"processed by my pipeline") # 4) 启动回放进行分析(成功后 controller 独立于 cap 存在) status, controller = cap.OpenCapture(rd.ReplayOptions(), None) if status != rd.ResultCode.Succeeded: print("Replay failed:", status) return None # 在这里使用 controller 分析帧...(略) controller.Shutdown() # 先关控制器 return controller finally: cap.Shutdown() # 后关捕获文件句柄 handle_capture("frame123.rdc")

小结

  • .rdc文件是 RenderDoc 捕获的容器格式,包含帧捕获正文、缩略图、调用栈信息及任意自定义 Section,其底层布局定义于 rdcfile.cpp;
  • CaptureAccess提供跨网络的受限能力子集,CaptureFile则在本地文件上提供完整能力(打开、转换、结构化数据、回放),两者都必须只在 replay thread 上访问;
  • OpenFile是轻量操作,只读元数据不发图形 API 调用;OpenCapture才真正启动回放,且需先关ReplayController再关CaptureFile
  • 自定义数据可以通过WriteSection/GetSectionContents以编程方式读写,也可通过将纯文本 ASCII Section 追加到文件末尾实现"零 API"注入,但后者必须严格遵循格式,否则会损坏捕获文件;
  • 更深入的主题可继续阅读 python_module(独立驱动 Replay API)、remote_replay(网络回放)、structured_data(结构化数据)与 threading(线程模型)。
  • 开发工具
  • 调试器
  • 图形学
  • GPU

【免费下载链接】renderdoc

RenderDoc is a stand-alone graphics debugging tool.

项目地址:https://gitcode.com/gh_mirrors/re/renderdoc
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询