鸿蒙PC迁移:RenderDoc 图形调试器鸿蒙PC适配全记录 一、写在前面欢迎加入鸿蒙PC开发者社区共同打造开发者工具生态鸿蒙PC开发者社区https://harmonypc.csdn.net/项目开源地址https://atomgit.com/OpenHarmonyPCDeveloper/ohos_renderdoc欢迎在PC社区平台申请新建项目https://atomgit.com/OpenHarmonyPCDeveloper这篇文章记录的是RenderDocv1.x在 HarmonyOS PC / 鸿蒙真机环境中的一次适配过程。RenderDoc 是图形开发者最常用的一款帧捕获式图形调试器它把一帧里所有的渲染调用draw call整帧抓下来然后离线回放逐个事件检查 GPU 状态——查看纹理 / 渲染目标、检查每个 draw 的完整管线状态、调试着色器、查看顶点输入输出、做像素历史分析等等。原版支持 Vulkan、D3D11、D3D12、OpenGL / OpenGL ES、Metal覆盖 Windows / Linux / Android。原版 RenderDoc 是一个 C / Qt 桌面项目主体由三块组成图形 API 捕获 / 回放核心库librenderdoc、Qt Widgets 图形界面qrenderdoc、命令行工具renderdoccmd。它和 Electron 项目完全不一样也不是 ArkUI 原生项目。和大多数 Qt 工具相比RenderDoc 还有一个额外的硬约束——它的 GUI 强制内嵌 Python 解释器通过一套自定义 SWIG 把整个回放 API 包装给 Python 用这让它比普通 Qt Widgets 应用难移植得多。适配鸿蒙 PC 时关键问题是怎样让一个 Qt Widgets 桌面程序进入鸿蒙 Stage 模型怎样让 nativelibentry.so作为 HAP 的 native library 被 QPA 插件加载并启动怎样让 Qt 界面绘制到鸿蒙 PC 窗口XComponent中怎样把 Qt for Harmony SDK、QPA 插件和 Qt 动态库打进 HAPqrenderdoc强依赖内嵌 Python 自定义 SWIG PySide2 librenderdoc图形核心这些在鸿蒙上都还没有完整 GUI 怎么落地怎样先做出一个可运行、可交互、长得像 RenderDoc 的鸿蒙 PC 版本。这条路线的目标很明确先把 RenderDoc 作为一个 Qt Widgets 应用稳定跑到鸿蒙 PC 真机上复刻它的主界面布局并保证交互可用再逐步评估完整qrenderdoc内嵌 Python SWIG 绑定 librenderdoc核心的深度迁移。二、项目背景RenderDoc 是 Qt/C 桌面应用适配前先看项目性质。RenderDoc 不是 Electron 应用也不是 WebView 应用。它的主体是 C / Qt 桌面程序原始桌面版本主要面向 Windows、Linux、Android。整个代码库大致分成几层捕获 / 回放核心renderdoc/约 2000 头文件、800 cpp里面driver/下是各图形后端d3d11 / d3d12 / gl / vulkan / metal的 hook 与回放实现。这是 RenderDoc 的灵魂也是最依赖具体平台和 GPU 驱动的部分。Qt Widgets 图形界面qrenderdoc/所有面板Texture Viewer / Pipeline State / Mesh Viewer / Event Browser / Python Shell 等都在qrenderdoc/Windows/。内嵌脚本层qrenderdoc通过CPython 自定义 SWIG 分支 PySide2/Shiboken把整套回放 API 暴露给 PythonGUI 自身也依赖这层和核心通信。这次适配新增的鸿蒙工程目录是renderdoc-1.x/ ├── renderdoc/ # 捕获/回放核心保持不动 ├── qrenderdoc/ # Qt Widgets 界面仅入口加一处 OHOS 守卫 ├── renderdoccmd/ # 命令行工具保持不动 ├── CMakeLists.txt # 上游桌面构建保持不动 └── harmony_pc/ # 鸿蒙侧新增工程桌面构建完全不受影响 ├── AppScope/ │ └── app.json5 # bundleName: org.renderdoc.qrenderdoc ├── build-profile.json5 # app 级签名 / product 配置 ├── hvigorfile.ts / oh-package.json5 ├── qtforharmony_sdk - ... # 工程自带的 Qt 5.15.12 for HarmonyOS软链 └── entry/ ├── build-profile.json5 # externalNativeOptions - CMake ├── libs/arm64-v8a/ # 打进 HAP 的 QPA 平台插件 .so └── src/main/ ├── module.json5 # deviceTypes: 2in1 / tablet ├── ets/ # ArkTS 桥 │ ├── abilitystage/MyAbilityStage.ets │ ├── entryability/EntryAbility.ets │ └── pages/Index.ets └── cpp/ ├── CMakeLists.txt # SHELL / FULL 双模式构建 └── renderdoc_ohos_shell.cpp # RenderDoc 界面外壳关键约束是上游 RenderDoc 源码基本保持不动所有鸿蒙相关的东西都放在harmony_pc/里所以桌面构建完全不受影响。唯一一处改动是给qrenderdoc/Code/qrenderdoc.cpp的main()加了一个Q_OS_OPENHARMONY守卫的qtmain别名为将来的 FULL 构建预留桌面路径不变。当前适配里最核心的文件是harmony_pc/entry/src/main/cpp/renderdoc_ohos_shell.cpp # Qt Widgets 界面外壳 harmony_pc/entry/src/main/cpp/CMakeLists.txt # SHELL / FULL 双模式构建 harmony_pc/entry/build-profile.json5 # CMake 参数 / abiFilters harmony_pc/AppScope/app.json5 # 包名与应用信息三、路线选择借鉴已有 Qt for Harmony 工程的启动管线适配 RenderDoc 时参考了之前几个 Qt → HarmonyOS 工程ok-minitube-master、ok-Natron-ohos、ohos_avogadrolibs、ok-krita-ohos。这里要特别区分参考的是这些项目的Qt for Harmony SDK、Stage 工程组织方式和 native Qt 启动管线而不是把它们的业务代码拿来跑。最终真机上安装的包始终是 RenderDocbundleName: org.renderdoc.qrenderdoc Ability: EntryAbility Module: entryAppScope/app.json5中应用信息配置为{ app: { bundleName: org.renderdoc.qrenderdoc, vendor: baldurk, versionCode: 1000000, versionName: 1.0, icon: $media:layered_image, label: $string:app_name } }鸿蒙工程的设备类型面向 PC / 平板deviceTypes: [ 2in1, tablet ]Qt SDK 通过entry/build-profile.json5的 CMake 参数传入externalNativeOptions: { path: ./src/main/cpp/CMakeLists.txt, arguments: -DQT_PREFIXqtforharmony_sdk -DRENDERDOC_FULL_APPAUTO, cppFlags: , abiFilters: [arm64-v8a] }这里QT_PREFIXqtforharmony_sdk是相对路径CMake 里会把它解析成harmony_pc/qtforharmony_sdk的绝对路径并校验lib/cmake/Qt5/Qt5Config.cmake是否存在。这一点是整个适配能跑起来的基础——QT_PREFIX必须指向 Qt for Harmony SDK否则 CMake 找不到Qt5Config.cmake、Qt Widgets 和 QPA 平台插件。工程用的是Qt 5.15.12 for HarmonyOSarm64-v8aOHOS SDK 18这个 SDK 体积约 295MB所以harmony_pc/qtforharmony_sdk用的是指向共享 SDK 的软链接避免把它塞进仓库CI / 移植到别的机器时换成真实拷贝即可。另一个关键参数是RENDERDOC_FULL_APP它决定了构建模式这是这次适配的核心设计模式取值编译什么状态SHELLAUTO/OFF默认只编renderdoc_ohos_shell.cpp——一个自包含的 Qt Widgets 界面外壳只链接 Qt5 Core/Gui/Widgets/Svg/Network/Concurrent/OpenGL不依赖 librenderdoc / Python / SWIG✅ 可运行FULLON编真实的qrenderdoclibrenderdoc。需要先把CPython、SWIG 绑定、librenderdoc交叉编译到 OpenHarmony目前缺这些前置条件CMake 会带提示直接报错 阻塞中AUTO会探测两个 FULL 模式的前置产物是否存在——harmony_pc/python-ohos/lib/libpython3.so和harmony_pc/librenderdoc-ohos/lib/librenderdoc.so——不存在就自动回退到 SHELL所以 HAP 永远能构建、能启动。本次选择的第一阶段路线是不重写 ArkUI 主界面不一次性迁内嵌 Python、SWIG 绑定和 librenderdoc 核心先用 Harmony Stage 工程承载 Qt Widgets用 C 写一个自包含的界面外壳 SHELL复刻 RenderDoc 主窗口布局绕过 Python / 图形核心真机验证窗口、菜单 / 工具栏、面板切换、事件树、纹理通道、缩放等交互再根据后续目标决定是否继续迁 Python SWIG 核心。这条路线适合快速验证 Qt 桌面工具在鸿蒙 PC 上的窗口、输入、构建、安装和运行链路。四、鸿蒙工程壳Stage Ability XComponent QPA → libentry.so鸿蒙侧仍然是标准 Stage 工程。整条启动管线是这样串起来的HarmonyOS 启动 org.renderdoc.qrenderdocEntryAbility - EntryAbility.onWindowStageCreate() 加载 pages/Index - Index.ets 构建一个 XComponentlibraryname: plugins_platforms_qopenharmony - EntryAbility 调用 qpa.startQtApplication(this) - QPA 插件在 XComponent 上创建绘制表面并 dlopen() libentry.so - 解析 libentry.so 导出的 extern C qtmain - qtmain - main() - QApplication - RenderDoc 主窗口渲染进这个表面Index.ets里的核心就是一个XComponent它的libraryname指向 QPA 平台插件XComponent({id:this.windowId,type:XComponentType.NODE,libraryname:plugins_platforms_qopenharmony}).width(100%).height(100%);MyAbilityStage.ets在工程启动时把 QPA 插件挂上去并通过onAcceptWant返回主窗口实例 keyonCreate():void{qpa.attachAbilityStage(this);}onAcceptWant(want:Want):string{// 首次启动返回 renderdoc_main_window...}EntryAbility.ets在窗口 stage 创建后真正拉起 Qt 应用importqpafromlibplugins_platforms_qopenharmony.so;asynconWindowStageCreate(windowStage:Window.WindowStage){...awaitthis.launchParamSet();// 可把 .rdc 抓帧文件路径作为 argv[1] 传进去this.startQtApplication();}privatestartQtApplication(){qpa.startQtApplication(this);// QPA 插件随后 dlopen libentry.so 并调用 qtmain}native 侧的CMakeLists.txt把入口编译成entry即libentry.so。SHELL 模式下只编一个源文件add_library(entry SHARED ${CMAKE_CURRENT_SOURCE_DIR}/renderdoc_ohos_shell.cpp) target_compile_features(entry PRIVATE cxx_std_17) target_compile_definitions(entry PRIVATE APP_NAMERenderDoc OPENHARMONY NAPI_DISABLE_CPP_EXCEPTIONS) set_target_properties(entry PROPERTIES BUILD_RPATH $ORIGIN) target_link_libraries(entry PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Svg Qt5::Network Qt5::Concurrent Qt5::OpenGL)这里有一个踩过的坑写进了注释链接Qt5::Gui会让 hvigor 的collectAllLibs把 Qt 的图片格式插件qgif/qico/qjpeg/qsvg一起打包而 qsvg 插件又依赖libQt5Svg.so所以必须同时链接Qt5::Svg否则 QPA 插件加载libentry.so时会失败真机上表现为白窗口。另外 QPA 平台插件本身是被 ArkTS 加载的不需要在这里链接。C 入口通过导出的qtmain被 QPA 插件调用intmain(intargc,char**argv){QApplicationapp(argc,argv);app.setApplicationName(QStringLiteral(RenderDoc));app.setStyleSheet(QString::fromUtf8(kStyle));// RenderDoc 风格深色主题QMainWindow window;// ... 搭建 Event Browser / Texture Viewer / Pipeline State / ...window.showMaximized();returnapp.exec();}#ifdefined(Q_OS_OPENHARMONY)// Qt OpenHarmony 平台插件 dlopen() libentry.so 后调用 qtmainexternCintqtmain(intargc,char*argv[]){returnmain(argc,argv);}#endif五、SHELL 界面外壳先做可运行的 MVP完整qrenderdoc Python 核心库的迁移不是第一阶段目标。为了先把真机可用链路跑通这次实现了一个自包含的 Qt Widgets 界面外壳renderdoc_ohos_shell.cpp复刻 RenderDoc 主窗口的布局和外观。它的主窗口是一个标准QMainWindow顶部菜单栏 工具栏中间用QSplitter三栏布局底部状态栏。当前 SHELL 外壳的界面包括菜单栏FileOpen Capture / Save Capture / Exit、Window、Tools、Help工具栏RenderDoc 橙色品牌名 Capture / Open Capture / Save 右侧绿色 API 按钮左栏 Event Browser事件树Frame #1024 → Shadow Pass / GBuffer Pass / Lighting Pass / Post Present带 EID 列上方有过滤框中栏分析标签页Texture Viewer棋盘格背景 自绘渐变纹理画布、RGBA 通道开关、Mip / Zoom 下拉、底部输出缩略图条Pipeline StateVTX→VS→HS→DS→GS→RS→PS→OM→CS 管线阶段按钮 资源/状态表Mesh Viewer顶点输入表VTX / IDX / POSITION / NORMAL / TEXCOORDPython Shell控制台预览真实交互式控制台需要内嵌 CPython见第六节右栏 Inputs / Outputs绑定的输入资源与渲染目标列表状态栏显示当前加载的抓帧、EID 等信息。整个外壳是 UI 演示性质它不链接 librenderdoc也不做真实抓帧 / 回放目的是把鸿蒙启动链路ArkTS → XComponent → QPA →libentry.so→qtmain→ 可见且可交互的 Qt 窗口端到端跑通并在真实qrenderdoc落地前先占位。六、为什么先做 SHELL而不是完整 qrenderdoc这是 RenderDoc 适配里最需要解释的一个决策也是它和别的 Qt 工具最不一样的地方。普通 Qt Widgets 工具把界面源码编进libentry.so、链接 Qt SDK 基本就能跑。但RenderDoc 的 GUI 不行——qrenderdoc/Code/qrenderdoc.cpp的main()直接#include pyrenderdoc/PythonContext.h也就是说GUI 强制内嵌 Python 解释器CPython一套RenderDoc 自定义的 SWIG 分支在构建时生成 C↔Python 绑定把整个回放 API 包装给界面用——这些绑定不是可选项而是 GUI 和核心通信的桥可选还要PySide2 / Shiboken让 Python 能操作 Qt 对象最终还要链接librenderdoc捕获 / 回放核心。而本地目前没有为 OpenHarmony 交叉编译的 Python没有 OHOS 版 SWIG工程自带的 Qt SDK 里也不含 PySide / Shiboken。直接编完整qrenderdoc是过不去的需要一条多阶段的前置链路已核实的“必做清单”为 arm64-ohos交叉编译 CPython产出libpython3.so和头文件在构建主机上跑RenderDoc 自定义 SWIG生成*_python.cxx绑定这步在主机跑不在目标设备再用上一步的 OHOS Python 编译为 OpenHarmony交叉编译librenderdoc先做回放侧Vulkan/GLES 捕获 hook 是强平台相关的留到后面PySide2 可先用QRENDERDOC_ENABLE_PYSIDE2OFF关掉只丢 Python 操作 Qt 的能力Python 控制台本身还在。为了先让应用跑起来、可演示SHELL 直接绕开了这一整套用纯 Qt Widgets 复刻界面不需要任何 Python / SWIG / 核心库就能在真机上交互运行。这和 Avogadro 适配里“先用 QPainter CPU 渲染绕开 OpenGL ES 移植”是同一种“先能跑、再优化”的工程取舍——Python SWIG 核心的深度迁移留给后续阶段。七、真机调试踩的两个坑外壳第一版装到真机后连续踩了两个很典型的坑都和“鸿蒙上的 Qt 行为”有关记录下来。坑 1缩放后文字 / 内容不放大也不缩小。改鸿蒙系统 / 窗口缩放比例时界面内容纹丝不动。原因是 Qt 默认按 device-pixel-ratio 1 渲染没启用高 DPI 缩放。修复办法是在QApplication构造之前打开高 DPI 属性注意顺序构造之后再设就无效了并用PassThrough让 1.25 / 1.5 这类小数倍率也生效#if(QT_VERSIONQT_VERSION_CHECK(5,14,0))QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);#endifQApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);QApplicationapp(argc,argv);// 必须在属性设置之后构造坑 2点任何按钮都没反应。这个不是鸿蒙输入问题而是第一版外壳是按“静态界面预览”写的绝大多数控件没有连接槽函数——只有菜单 Exit、可勾选按钮的勾选态、标签页切换是默认能动的。判断依据是参考的 minitube 是个能正常点击的真实应用用的是完全相同的 XComponent 接法说明输入链路本身是通的缺的只是处理逻辑。修复办法是给每个可交互控件都接上可见反馈工具栏 / 菜单 → 状态栏提示RGBA 通道按钮 → 真正改变纹理画布事件树点击 → 显示 EID 并更新画布标题API 按钮 → 在 Vulkan / D3D11 / D3D12 / OpenGL 间循环Mip / Zoom 下拉、列表点击、Python 输入框回车都给反馈。这两个坑也顺带说明了一件事真机上验证“缩放 点击”是判断 Qt 应用是否真正活起来的最快方式。八、构建、安装和真机运行本次工程路径/XM/renderdoc-1.x/harmony_pc构建时需要指定 DevEco SDK 环境变量否则可能遇到 hvigor / SDK 版本不匹配问题例如(0 , hvigor_1.WithParamReplacement) is not a function最终使用的构建命令是cd/XM/renderdoc-1.x/harmony_pcenvDEVECO_SDK_HOME/Applications/DevEco-Studio.app/Contents/sdk\OHOS_BASE_SDK_HOME/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony\/Applications/DevEco-Studio.app/Contents/tools/node/bin/node\/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js\--modemodule-pmoduleentry-pproductdefault assembleHap\--analyzenormal--parallel--incremental--no-daemon--stacktrace注意首次在 DevEco Studio 打开harmony_pc/时需要为org.renderdoc.qrenderdoc这个包名配置签名签名 profile 是和包名绑定的不能复用别的工程的签名。native 部分的 CMake 构建由entry/build-profile.json5 → externalNativeOptions驱动会把-DQT_PREFIXqtforharmony_sdk -DRENDERDOC_FULL_APPAUTO传给CMakeLists.txt只编 arm64-v8a。签名后的 HAP 路径harmony_pc/entry/build/default/outputs/default/entry-default-signed.hap安装到真机/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdcinstall-r\/XM/renderdoc-1.x/harmony_pc/entry/build/default/outputs/default/entry-default-signed.hap启动应用/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc shell aa start\-borg.renderdoc.qrenderdoc\-aEntryAbility强停应用/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc shell aa force-stop org.renderdoc.qrenderdoc查看安装信息确认包名 / ABI / SDK 版本/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc shell bm dump-norg.renderdoc.qrenderdoc关键输出应包含bundleName: org.renderdoc.qrenderdoc versionName: 1.0 cpuAbi: arm64-v8a九、真机验证流程最终真机验证按下面这条路径走安装 HAP - 启动 RenderDocaa start - 默认显示 Event Browser Texture Viewer - 点左侧 Event Browser 的事件如 EID 142- 观察底部状态栏更新 - 点中间标签页切到 Pipeline State / Mesh Viewer - 在 Texture Viewer 里点 R / G / B / A 通道开关 - 看纹理画布颜色变化 - 点右上角 API 按钮 - 在 Vulkan / D3D11 / D3D12 / OpenGL 间切换 - 改系统 / 窗口缩放比例 - 看文字和面板是否跟着放大缩小切换标签页和点击事件树是验证“面板交互 状态更新”是否正常的最直接方式RGBA 通道开关、API 按钮和缩放则验证鼠标事件、重绘和高 DPI 是否都正确传到了 Qt 窗口。十、当前版本边界和后续方向这次适配完成的是 RenderDoc 在鸿蒙 PC 真机上的第一阶段可运行版本SHELL。它已经解决了几个关键问题HarmonyOS Stage 工程接入工程自带 Qt for Harmony SDKQt 5.15.12 / arm64-v8a接入ArkTS → XComponent → QPA →libentry.so→qtmain的完整启动管线Qt Widgets native 应用启动与窗口显示链接Qt5::Svg解决 qsvg 插件依赖导致的白窗口问题高 DPI 缩放内容随系统缩放放大 / 缩小全界面交互菜单 / 工具栏 / 事件树 / 标签页 / 纹理通道 / API 切换RenderDoc 包名和应用信息替换HAP 构建、签名、安装用纯 Qt Widgets 外壳绕开 Python / SWIG / 核心移植先把应用跑起来。后续方向roadmapPhase 2 — 交叉编译 Python-for-OHOS为 arm64-ohos 编出libpython3.so 头文件放到harmony_pc/python-ohos/这是 FULL 模式的第一个硬阻塞。Phase 3 — 生成 SWIG 绑定并接核心在主机上跑 RenderDoc 自定义 SWIG 生成绑定交叉编译librenderdoc先回放侧到harmony_pc/librenderdoc-ohos/两个前置产物就位后RENDERDOC_FULL_APPAUTO会自动从 SHELL 切到 FULL。Phase 4 — 捕获后端可选 / 长期评估在鸿蒙上做 Vulkan / GLES 的捕获 hook让 RenderDoc 不只是回放别处抓的.rdc还能直接抓鸿蒙本机应用的帧。十一、总结这次 RenderDoc 适配不是一次简单的套壳也不是 Electron 项目的资源搬运。它更接近一次 Qt 桌面工具在鸿蒙 PC 上的 native 运行验证而且因为 RenderDoc 的 GUI 强制内嵌 Python 自定义 SWIG它比一般 Qt Widgets 工具更能体现“依赖链很深的桌面工具在鸿蒙上要怎么分阶段落地”这个现实约束下的工程取舍。整个过程可以概括为识别原项目技术栈Qt Widgets 内嵌 Python/SWIG librenderdoc 图形核心 - 新增 harmony_pc Stage 工程上游源码基本保持不动 - CMake 接入工程自带 Qt for Harmony SDKCore/Gui/Widgets/Svg/Network/Concurrent/OpenGL - 设计 SHELL / FULL 双模式先编可运行的 SHELL - 用纯 Qt Widgets 外壳绕开 Python / SWIG / 核心移植 - 修高 DPI 缩放 补全控件交互 - 替换包名和应用信息 - 构建签名 HAP - 安装到鸿蒙 PC 真机验证界面与交互最终结果是一个以org.renderdoc.qrenderdoc为包名的 RenderDoc Harmony PC 应用可以在真机上启动、显示出 RenderDoc 的主界面布局Event Browser / Texture Viewer / Pipeline State / Mesh Viewer并支持菜单、工具栏、事件树、标签页切换、纹理通道开关、API 切换和高 DPI 缩放等完整交互。虽然它还不是完整上游 RenderDoc内嵌 Python、SWIG 绑定与 librenderdoc 核心仍在迁移中但已经具备了第一阶段可运行、可演示、可继续扩展的基础。