RenderDoc 移植到 SpacemiT K3 (Bianbu) 实战指南

本文记录了在 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/capmatch/captured
QPalette 枚举改名 Qt6 移除旧名 BackgroundWindow, ForegroundWindowText
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/elapsedQElapsedTimer
QTextCodec 移出 QtCore #include <QtCore5Compat/QTextCodec> + .proQT += 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 项目。它的产物
    路径(.proDESTDIR=$$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 方式(推荐)

  1. 桌面终端启动 ./bin/qrenderdoc

  2. 菜单 File → Launch Application

  3. 选择要抓帧的 GLES 应用(示例:/usr/bin/glmark2-es2,系统自带;自编译的 glmark2-es2-wayland 等 Wayland 原生应用同样适用)

  4. 勾选 Queue Capture,设置捕获帧号(如第 1 帧)

  5. 点击 Launch,应用启动并渲染,到达指定帧自动捕获

  6. 捕获文件自动保存到 /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 失效的原因(两点叠加):

  1. Wayland 按键支持未编译:RenderDoc 的按键捕获基于 X11(linux_stringio.cppXK_F12),实验性 Wayland 按键支持默认关闭(ENABLE_UNSUPPORTED_EXPERIMENTAL_POSSIBLY_BROKEN_WAYLAND=OFF

  2. 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.proQT += core5compat
链接报 libatomic 符号缺失 Bianbu qmake 配置缺 -latomic .proQMAKE_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” 上游硬编码标签,与架构无关 忽略

本文基于实际移植过程整理,欢迎在评论区提问或补充其他平台的移植经验。