ARTICLE DETAIL

资讯详情

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

RenderDoc 扩展开发实战:用 CaptureViewer 接口监听捕获与事件变更回调

RenderDoc 扩展开发实战:用 CaptureViewer 接口监听捕获与事件变更回调 RenderDoc 扩展开发实战用 CaptureViewer 接口监听捕获与事件变更回调【免费下载链接】renderdocRenderDoc is a stand-alone graphics debugging tool.项目地址: https://gitcode.com/gh_mirrors/re/renderdoc导读在开发 RenderDoc 的 UI 扩展UI Extension时最常见的需求是当用户在界面中加载/关闭捕获Capture、切换当前事件Event时扩展需要第一时间收到通知并同步更新自身的界面与状态。RenderDoc 为此提供了qrenderdoc.CaptureViewer接口——它与扩展插件机制协同工作是内部几乎所有面板如 API Inspector、Event Browser、Texture Viewer 等刷新自身状态所依赖的统一回调通道。读完本文你将掌握CaptureViewer的完整用法如何继承接口、如何注册/注销 Viewer、四个回调各自的触发时机与语义差异以及从 C 侧ICaptureViewer定义到 Python 桥接层SWIG的底层实现原理。CaptureViewer 是什么UI 扩展的事件订阅接口CaptureViewer是一个供 Python 脚本继承并实现的接口用于接收与捕获生命周期和事件选择相关的一系列通知。其 C 侧的底层定义位于 qrenderdoc/Code/Interface/QRDInterface.h对应的 C 结构为ICaptureViewer文档注释中明确写道An interface implemented by any object wanting to be notified of capture events.在 UI 内部几乎所有面板都通过实现这个接口来同步自身状态——文档原文指出This is used internally for most panels to update their state to reflect newly selected events and react to captures opening and closing.这意味着当你编写扩展时采用的正是与官方内置面板完全相同的机制行为表现与一致性有天然保证。该接口并不特定于 UI 扩展——脚本同样可以添加 CaptureViewer。但官方文档特别强调从脚本添加时需要格外谨慎因为对象的生命周期更难管理。Viewer 一旦被添加就会持续接收事件直到被移除如果脚本中的对象引用丢失不再可访问后续将难以将其移除。因此除非确有必要建议将 Viewer 的生命周期与扩展的注册/注销register/unregister回调严格绑定。最小可运行示例从继承到注册下面是最小可用的完整示例直接取自 docs/python_api/in_depth/frame_viewers.rstimport qrenderdoc as qrd class Viewer(qrd.CaptureViewer): def OnCaptureLoaded(self): print(A new capture was loaded) def OnCaptureClosed(self): print(The capture was closed) def OnSelectedEventChanged(self, eventId): print(fThe selected event is {eventId}) def OnEventChanged(self, eventId): print(fThe current event is {eventId}) view Viewer() def register(version, ctx): ctx.AddCaptureViewer(view) def unregister(): pyrenderdoc.RemoveCaptureViewer(view)要点拆解实例创建view Viewer()创建一次即可register时将其注册进上下文ctx。注册入口register(version, ctx)是 UI 扩展的标准初始化入口ctx为CaptureContext其AddCaptureViewer方法在 C 侧声明于 qrenderdoc/Code/Interface/QRDInterface.h对应ICaptureContext::AddCaptureViewer。注销入口unregister()中通过pyrenderdoc.RemoveCaptureViewer(view)移除 Viewer对应ICaptureContext::RemoveCaptureViewer见 qrenderdoc/Code/Interface/QRDInterface.h。警告必须显式调用父类构造器如果你在子类中自定义了__init__必须显式调用super().__init__()。文档中明确警告If you implement an__init__function to construct your object, it is important that you explicitly call the parent class constructor withsuper().__init__()otherwise the interface will be left partially initialised and will likely crash when passing the object to C.原因可以从桥接层代码得到印证在 qrenderdoc/Code/pyrenderdoc/qrenderdoc.i 中Python 侧的CaptureViewer由包装器PythonCaptureViewer实现构造时会逐个查询并绑定OnCaptureLoaded、OnCaptureClosed、OnSelectedEventChanged、OnEventChanged这四个方法。若 Python 侧对象初始化不完整C 侧拿到的将是部分初始化的接口指针传入 C 时极易引发崩溃。因此只要定义了__init__第一行务必是super().__init__()。警告卸载扩展时必须移除 Viewer重载reload扩展时如果unregister中没有移除旧 Viewer旧的 Viewer 会继续存活并持续收到回调——这会导致重复通知、内存泄漏和状态污染。所以注册与注销必须成对出现这是官方文档明确强调的纪律。回调语义详解捕获的加载与关闭Viewer 会收到两类生命周期回调OnCaptureLoaded()捕获加载完成后立刻调用。此时捕获仍处于打开状态因此在回调内部可以安全地调用 replay 函数例如通过ctx.Replay()获取ReplayController访问管线状态、资源等。OnCaptureClosed()捕获关闭前立刻调用此时捕获同样仍处于打开状态查询接口依然会返回包含捕获状态的结果。C 侧的定义与注释见 qrenderdoc/Code/Interface/QRDInterface.h其中对两个回调给出了更细的行为约束OnCaptureLoaded在 UI 仍在填充面板的过程中被调用各 Viewer包括 UI 面板之间的调用顺序是未定义的所以建议在该回调中只做初始化和对底层捕获信息的访问不要做繁重工作。OnCaptureClosed在 UI 正在关闭的过程中被调用强烈建议只做资源释放和缓存清理。如果需要在捕获完全关闭之后执行代码应使用延迟回调delayed callback它保证在捕获彻底关闭后才被触发。源码视角回调究竟在何时触发从 qrenderdoc/Code/CaptureContext.cpp 可以看到这些回调的实际调用点加载路径约 L1019-L1034CaptureContext在捕获装载完成后先通过SetEventID将事件收敛到一致位置优先使用m_LastAction记录的事件否则取m_Actions中最后一个事件再通过GUIInvoke::blockcall在主线程上遍历所有已注册 Viewer 并逐个调用OnCaptureLoaded()。关闭路径约 L1536-L1542CloseCapture()中先拷贝一份 Viewer 列表对每个仍注册在案的 Viewer 调用OnCaptureClosed()随后才清理文件监视器、RGP 会话、捕获文件路径等内部状态。事件变更路径RefreshUIStatus约 L1840-L1851SetEventID驱动的 UI 刷新会遍历 Viewer按需调用OnSelectedEventChanged(m_SelectedEventID)与OnEventChanged(m_EventID)并支持通过exclude参数排除特定 Viewer避免重复通知。这套实现印证了文档的关键承诺回调都在捕获打开期间触发加载后、关闭前因此回调内对捕获状态与 replay 接口的访问在时序上是安全的。Selected Event 与 Current Event两个回调的本质区别OnEventChanged与OnSelectedEventChanged都携带一个eventId参数但二者的语义有细微而重要的差别。要理解这个差别必须先明确 RenderDoc 中选中事件与当前事件两个概念详见 docs/python_api/in_depth/curevent.rst选中事件Selected Event用户实际点击/选择的那个事件 ID。当选中一个包含大量子事件的marker 区域region时这个 ID 就是 marker 根节点本身通常其数值比子事件更小。当前事件Current Event即有效事件effective event是管线状态快照真正取自的位置。选中 marker 区域时有效事件被设定为该区域内所有子事件执行完毕之后的那个位置——也就是说选中一个包含多次 Draw 的渲染 Pass 区域看到的将是整个区域渲染完成后的最终结果。由此得到回调的精确语义对应 C 侧注释 qrenderdoc/Code/Interface/QRDInterface.hOnEventChanged(eventId)有效当前事件发生变化时调用。这是大多数场景下应该使用的回调——当前管线状态所基于的事件变了。OnSelectedEventChanged(eventId)选中事件发生变化时调用。它反映的是用户实际点中的那个 ID通常与有效事件一致但不一定总是一致。官方注释举例API InspectorAPI 查看器正是利用它来展示到 marker 区域为止的 API 事件列表。一个容易踩坑的边界情形文档明确指出由于上述差异选中事件可能在不改变当前事件的情况下发生变化。典型场景用户先选中某个 marker 区域的根节点——此时有效事件变成该区域内最后一个子事件两个回调都会被触发用户随后再选中该区域内的最后一个事件——此时选中事件变了但有效事件没有变它本来就是最后一个子事件。在这个序列的第二步中只有OnSelectedEventChanged被调用没有对应的OnEventChanged。如果你的扩展逻辑只监听OnEventChanged来同步选中位置就会漏掉这次变化。这也是文档建议默认优先使用OnEventChanged仅在需要感知选中粒度的场景使用OnSelectedEventChanged的原因。初始状态自动同步无需自己判断捕获是否打开CaptureViewer还有一个非常体贴的约定如果注册 Viewer 时已经有一个捕获处于打开状态那么OnCaptureLoaded、OnEventChanged、OnSelectedEventChanged会立即被调用一次。这意味着你不需要在register里手动检测当前是否有捕获打开并自行初始化新注册的 Viewer 会立刻拿到当前捕获与当前事件保证与既有面板的状态同步。这一行为在 C 加载流程中同样有据可查CaptureContext在加载捕获时会先通过SetEventID把选中/当前事件统一到确定值随后才广播OnCaptureLoaded从而保证 Viewer 首次收到通知时事件状态是自洽且确定的见 qrenderdoc/Code/CaptureContext.cpp。线程与安全注意事项文档提醒不建议在捕获关闭期间执行任何 replay 调用——这些调用未必全部安全而且无法保证所有异步 replay 调用都会被处理。结合 docs/python_api/in_depth/threading.rst 的说明可以更好理解这一点RenderDoc 运行时有两条主线程处理 UI 交互的UI 线程以及承担大多数重放工作的replay 线程。UI 扩展的 Python 代码运行在UI 线程上可直接访问控件与其他面板。大部分 replay 工作以异步方式在 replay 线程上执行避免长时间阻塞 UI。因此回调中的安全实践是OnCaptureLoaded内可安全地发起对底层捕获信息的访问与初始化但避免耗时操作。OnCaptureClosed内只做资源释放与缓存清理如需在捕获完全关闭后执行代码请使用延迟回调delayed callback。异步 replay 调用若在回调内发起异步 replay要意识到其执行时机不受回调返回控制不要在关闭阶段依赖其结果。实战清单将 CaptureViewer 落地到自己的扩展把以上要点整理成一份可直接照做的清单继承并实现class MyViewer(qrd.CaptureViewer)按需覆写四个回调方法名与签名务必与接口一致OnCaptureLoaded()、OnCaptureClosed()、OnSelectedEventChanged(eventId)、OnEventChanged(eventId)。若自定义__init__第一行调用super().__init__()避免 C 侧拿到部分初始化的接口而崩溃。注册在扩展的register(version, ctx)中调用ctx.AddCaptureViewer(view)。注销在扩展的unregister()中调用pyrenderdoc.RemoveCaptureViewer(view)防止扩展重载后旧 Viewer 残留并持续接收回调。选对回调同步当前渲染状态用OnEventChanged需要感知用户选中的 marker 根节点粒度如实现类 API Inspector 的列表展示用OnSelectedEventChanged并做好选中事件变而当前事件不变的边界处理。遵守回调纪律加载回调中避免重活关闭回调中避免 replay 调用必要时改用延迟回调。善用初始同步依赖注册时若已有捕获则立即收到初始通知的特性无需自行检测捕获是否打开。按照这套流程你的扩展就能与 RenderDoc 内置面板一样准确感知捕获的打开/关闭与事件切换进而实现状态同步、缓存管理、事件过滤等一系列进阶功能。延伸阅读当前帧事件Selected vs Current Event详解理解 eventId 语义与 marker 区域选择行为的基础。RenderDoc UI 的线程模型了解 UI 线程与 replay 线程的分工以及异步调用在回调中的注意事项。UI 扩展开发指南掌握register/unregister入口与扩展的完整生命周期。qrenderdoc 接口参考查看CaptureViewer、CaptureContext的完整 API 文档。底层接口定义可进一步查阅 qrenderdoc/Code/Interface/QRDInterface.hICaptureViewer与ICaptureContext以及 qrenderdoc/Code/pyrenderdoc/qrenderdoc.iPython 桥接实现。【免费下载链接】renderdocRenderDoc is a stand-alone graphics debugging tool.项目地址: https://gitcode.com/gh_mirrors/re/renderdoc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表