本文记录了在 SpacemiT K3(Bianbu Linux, riscv64)上从源码构建 RenderDoc 并跑通抓帧的完整过程。
所有命令均在文中标注的硬件/软件环境下验证过,可直接照做。
核心工作量是 Qt5 → Qt6 的 GUI 移植。
1. 为什么值得做这件事
RenderDoc 是图形开发的事实标准调试器:抓帧、逐 draw call 回放、查纹理/缓冲/着色器状态。
但官方只发布 x86 / ARM 二进制,RISC-V 平台上想要用它,只能自己移植。
它为什么能移植? 这里有个关键事实:RenderDoc 的 Linux 捕获路径是 LD_PRELOAD 拦截图形 API
调用(不涉及任何汇编级 inline hook),回放走真实 GPU 驱动(K3 上是 PowerVR BXM-4-64),
而最让人担心的"软件回放"其实是纯 C++ 的着色器解释器(逐条 switch opcode 执行),
与 CPU 架构无关。也就是说,RISC-V 上真正架构相关的代码面很小。
更幸运的是,上游已经在为 riscv64 铺路:
-
renderdoc/common/globalconfig.h的 64 位架构宏列表包含__riscv64; -
renderdoc/os/posix/linux/linux_process.cpp已实现 RISC-V 的 ebreak 软件断点支持。
所以这个移植的本质是:核心库编译零改动通过,剩下的活集中在 Qt GUI 从 Qt5 迁到 Qt6。
2. 环境与前置准备
| 项目 | 值 |
|---|---|
| 开发板 | SpacemiT K3 Pico ITX |
| 系统 | Bianbu 4.0.3 |
| 内核 | 6.18.3-generic (riscv64) |
| 显示栈 | labwc (Wayland) + Xwayland |
| 编译器 | GCC 15.2.0 |
| CMake | 4.2.3 |
| Ninja | 1.13.2 |
| Qt | 6.10.2 |
安装依赖:
sudo apt-get update
sudo apt-get install -y cmake ninja-build \
libxcb-keysyms1-dev \
qt6-base-dev qt6-tools-dev qt6-svg-dev qt6-5compat-dev
libxcb-keysyms1-dev必装:缺失时 CMake configure 阶段直接 abort(FindPkgConfig 找不到 xcb-keysyms)。
qt6-5compat-dev必装:qrenderdoc 依赖的QTextCodec在 Qt6 中移到了 QtCore5Compat 模块。
3. 构建核心库(无 GUI,先跑通主链路)
git clone https://github.com/baldurk/renderdoc.git
cd renderdoc
git checkout 4e59f62a9 # 本文基于 v1.45-87-g4e59f62a9,新版本应同样适用
git switch -c riscv-qt6-port # 建一个新分支
mkdir -p build-riscv && cd build-riscv
cmake .. -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_QRENDERDOC=OFF \
-DENABLE_PYRENDERDOC=OFF
cmake --build .
产出:
bin/renderdoccmd— 命令行捕获/注入工具lib/librenderdoc.so— 核心 replay + hook 库
验证:
$ file bin/renderdoccmd
ELF 64-bit LSB pie executable, UCB RISC-V, RVC, double-float ABI, ...
$ ldd bin/renderdoccmd | grep "not found" # 空 = 依赖全满足
$ ./bin/renderdoccmd version
renderdoccmd x64 v1.46 built from 4e59f62a96f2b571ba830cd1ccfc2b5c5cd7ae48
APIs supported at compile-time: Vulkan, GL, GLES.
注意
version输出里的 “x64” 是上游硬编码的显示标签,与架构无关,可忽略。
这里刻意先关掉 GUI 构建:核心库零源码改动编译通过,说明 RISC-V 支持是真的,
后续排障范围可以放心地收敛到 GUI 层。
4. Qt5 → Qt6 移植(全文唯一的源码改动)
Bianbu 只提供 Qt6,而上游 qrenderdoc 是 Qt5 代码(Qt5/Qt6 的 API 破坏性变更很多),
因此需要一份移植补丁。已整理为单个 git patch,可直接应用:0001-Port-qrenderdoc-UI-to-Qt6.zip (19.1 KB)
- 补丁文件:附件为 zip 压缩包,解压后得到
0001-Port-qrenderdoc-UI-to-Qt6.patch(约 81KB,62 个文件,+362/-142 行) - 基准:补丁基于上游
4e59f62a9,在步骤 3 的 clone 上直接应用即可
把补丁文件放到仓库根目录后执行(第 3 章结束时你在 build-riscv/ 里,需先回到仓库根目录):
cd .. # 回到仓库根目录(若已在此目录则跳过)
git am 0001-Port-qrenderdoc-UI-to-Qt6.patch
变更摘要
按 API 类别归类(完整逐文件清单见补丁本身):
| 类别 | 说明 | 典型替换 |
|---|---|---|
| QRegExp → QRegularExpression | Qt6 移除 QRegExp | indexIn/cap → match/captured |
| QPalette 枚举改名 | Qt6 移除旧名 | Background→Window, Foreground→WindowText |
| QStyleOption::init | Qt6 移除 | init(w) → initFrom(w) |
| QFontMetrics::width | Qt6 移除 | width() → horizontalAdvance() |
| Layout API | Qt6 变更 | setMargin(n) → setContentsMargins(n,n,n,n) |
| QWheelEvent | Qt6 新 API | pos()→position(), delta()→angleDelta().y() |
| QTime → QElapsedTimer | 单调计时 | QTime::start/elapsed → QElapsedTimer |
| QTextCodec | 移出 QtCore | #include <QtCore5Compat/QTextCodec> + .pro 加 QT += core5compat |
| QDesktopWidget | Qt6 移除 | screenAt()->availableGeometry() |
| rdcstr/QString 歧义 | Qt6 收紧隐式转换 | 显式 QString::fromUtf8(x.c_str()) |
| QVector 废弃 | Qt6 中 QVector 废弃 | rdcarray.h 加 #if QT_VERSION 条件编译 |
| 杂项 | MidButton→MiddleButton, drawRoundRect→drawRoundedRect, ImMicroFocus→ImCursorRectangle, flags() 返回 Qt::NoItemFlags 等 | — |
如果你的目标是最新上游代码,diff 可能会轻微偏移——但改动类别是稳定的,
拿着这张表对着编译错误逐个替换即可,都是机械性工作。
构建 GUI
cd build-riscv # 从仓库根目录进入第 3 章创建的构建目录
cmake .. -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_QRENDERDOC=ON \
-DENABLE_PYRENDERDOC=OFF \
-DQMAKE_QT5_COMMAND=/usr/bin/qmake6
cmake --build .
两个 Bianbu 发行版特有的坑,遇到再处理:
坑 1:手动跑 qmake 报错 “please set the Build Environment Variable CMAKE_DIR”
- 现象:改过
.pro后想手动跑qmake6快速重新生成 Makefile,直接报错退出 - 原因:qrenderdoc 是 CMake 挂出来的 qmake 子项目,不是独立 qmake 项目。它的产物
路径(.pro里DESTDIR=$$CMAKE_DIR/bin)和要 include 的 CMake 生成文件
(qrenderdoc_cmake.pri)都依赖CMAKE_DIR这个变量。正常流程里 CMake 调用 qmake
时会自动带上这个变量,但手动跑 qmake 时它是空的——.pro文件对此有硬性检查
(isEmpty(CMAKE_DIR) { error(...) }),所以直接报错 - 修法:命令行显式传变量(
CMAKE_DIR指向你自己的 CMake 构建目录,即第 3 章创建的build-riscv):
qmake6 CMAKE_DIR=/path/to/renderdoc/build-riscv
产物会落到该构建目录的 bin/ 下,与 CMake 流程保持一致
坑 2:链接阶段报 libatomic 相关错误 / qmake 报 QT.core.uses = libatomic 相关错误
- 现象:qmake 生成 Makefile 时或链接 qrenderdoc 时,报 libatomic 相关错误
- 原因:qmake 有"变量接力"机制——Qt 的模块描述文件(qt_lib_core.pri)声明
QtCore 依赖 libatomic,通过QMAKE_LIBS_LIBATOMIC变量传给链接器。但 Bianbu 打包
Qt6 时,平台配置(mkspec)漏了给这个变量赋值,接力棒没人接,qmake/链接器就找不到
-latomic。RISC-V 上 GCC 对部分原子操作(如 64 位原子加减)会退化成调用 libatomic
的软件实现,所以 QtCore 确实需要链接它 - 修法:在 qrenderdoc 的
.pro文件里补上这一棒(当前移植补丁已包含):
isEmpty(QMAKE_LIBS_LIBATOMIC): QMAKE_LIBS_LIBATOMIC = -latomic
若链接仍报缺符号,多半是系统只有运行时库 libatomic.so.1、缺开发链接名
libatomic.so,手动补一个符号链接:ln -s /usr/lib/riscv64-linux-gnu/libatomic.so.1 /usr/lib/riscv64-linux-gnu/libatomic.so
产出 bin/qrenderdoc(约 18MB,RISC-V ELF,未 strip)。
5. 部署
产物集中放到用户目录(/root 是 0700,桌面用户访问不了,必须放 /home 下):
mkdir -p /home/bianbu/renderdoc/{bin,lib}
cp bin/qrenderdoc bin/renderdoccmd /home/bianbu/renderdoc/bin/
cp lib/librenderdoc.so /home/bianbu/renderdoc/lib/
chown -R bianbu:bianbu /home/bianbu/renderdoc
最终结构:
/home/bianbu/renderdoc/
.
├── bin
│ ├── qrenderdoc
│ └── renderdoccmd
└── lib
└── librenderdoc.so
为什么不需要 LD_LIBRARY_PATH:构建时 qrenderdoc 自带
RUNPATH=$ORIGIN:$ORIGIN/../lib/,动态链接器会优先按 RUNPATH 就近找库,
从任何目录启动都能找到自己的 lib/librenderdoc.so,部署即自包含。
桌面终端直接运行:
cd /home/bianbu/renderdoc
./bin/qrenderdoc
6. 抓帧实战
6.1 GUI 方式(推荐)
-
桌面终端启动
./bin/qrenderdoc -
菜单 File → Launch Application
-
选择要抓帧的 GLES 应用(示例:
/usr/bin/glmark2-es2,系统自带;自编译的glmark2-es2-wayland等 Wayland 原生应用同样适用) -
勾选 Queue Capture,设置捕获帧号(如第 1 帧)
-
点击 Launch,应用启动并渲染,到达指定帧自动捕获
-
捕获文件自动保存到
/tmp/RenderDoc/<appname>_<时间戳>.rdc
GUI 方式通过"到达指定帧自动触发"抓帧,不依赖按键,因此对 Wayland 原生应用同样有效——这是当前环境下唯一可靠的抓帧路径。
在 qrenderdoc 中打开捕获的 .rdc 文件,即可逐 draw call 回放、查看纹理/缓冲/着色器:
6.2 命令行方式(当前不可用,待解决)
cd /home/bianbu/renderdoc/bin
./renderdoccmd capture --working-dir=/home/bianbu \
-c /home/bianbu/cap.rdc /usr/bin/glmark2-es2
当前限制:命令行方式的捕获依赖 F12 按键触发,而在 Bianbu 的 Wayland 环境下 F12 触发暂不生效——因此命令行 capture 目前实际上用不了,抓帧请使用 6.1 的 GUI 方式。
F12 失效的原因(两点叠加):
-
Wayland 按键支持未编译:RenderDoc 的按键捕获基于 X11(
linux_stringio.cpp的XK_F12),实验性 Wayland 按键支持默认关闭(ENABLE_UNSUPPORTED_EXPERIMENTAL_POSSIBLY_BROKEN_WAYLAND=OFF) -
EGL EXT hook 缺失:
eglGetPlatformDisplayEXT未被 hook,Wayland 应用初始化 EGL 走 EXT 路径时,键盘初始化代码(Keyboard::UseXlibDisplay)根本不会执行
状态:待解决。可行的解决方向(任选其一):
-
重新编译时开启实验性 Wayland 按键支持(
-DENABLE_UNSUPPORTED_EXPERIMENTAL_POSSIBLY_BROKEN_WAYLAND=ON) -
给
eglGetPlatformDisplayEXT补上 hook(与现有eglGetPlatformDisplay/eglGetDisplay相同模式)
解决后,命令行方式即可用于自动化批量抓帧场景。
7. 常见问题排查
| 现象 | 原因 | 处理 |
|---|---|---|
| CMake configure 直接失败 | 缺 xcb-keysyms 开发包 | apt install libxcb-keysyms1-dev |
| qrenderdoc 编译报 QTextCodec 找不到 | 缺 QtCore5Compat | apt install qt6-5compat-dev,.pro 加 QT += core5compat |
| 链接报 libatomic 符号缺失 | Bianbu qmake 配置缺 -latomic |
.pro 补 QMAKE_LIBS_LIBATOMIC = -latomic |
| GUI 里点击 capture 后没有生成 .rdc | /tmp/RenderDoc/ 属主不是当前用户(如 root 残留),写入静默失败,无任何报错 |
sudo chown bianbu:bianbu /tmp/RenderDoc;注意这个坑会复发:/tmp 是 tmpfs,重启即清空,之后若有 root 进程(如 SSH 下跑测试)重建该目录,属主又变回 root。根治:sudo chmod 1777 /tmp/RenderDoc(sticky 位 + 全局可写,与 /tmp 本身一致,之后谁重建都能写) |
| 命令行 capture 按 F12 没反应 | Wayland 环境:Wayland 按键支持未编译(ENABLE_UNSUPPORTED_EXPERIMENTAL_POSSIBLY_BROKEN_WAYLAND=OFF)+ eglGetPlatformDisplayEXT 未 hook |
用 6.1 的 GUI 方式(Queue Capture 不依赖按键);或按 6.2 列出的方向重编译/补 hook 解决 |
renderdoccmd version 显示 “x64” |
上游硬编码标签,与架构无关 | 忽略 |
本文基于实际移植过程整理,欢迎在评论区提问或补充其他平台的移植经验。

