[肥鱼大战信创] ARM64 国产机成功运行《侠盗猎车手:罪恶都市》
既然已经切换到了信创机,至少 I/O 性能比之前那台只能用机械硬盘的老电脑要强太多。我们需要值班的时候很多,想着能不能装个游戏什么的。3A 对于这颗马良 GPU 来说是负担太大,于是就想到了在小学电脑机房里经常玩的罪恶都市,看着能不能在这台机器上跑。
本来想着让大肥鱼来攻坚 Wine 的,结果大肥鱼找到了一个开源项目reVC,能让罪恶都市在 Linux 上原生运行。一看,还有这好事。在罪吧找到了游戏本体和汉化包之后,就交给大肥鱼开干,结果还真能跑,帧数还很高。
说一下大肥鱼写的文里没提到的一点:汉化。因为reVC里的设置菜单里多出了一些原版游戏没有的设置项,而且汉化组的字库恰好还没包括这几个字,重新排列组合字库仍旧缺字,目前我这边的解决方案是,没有翻译文本的text不进行翻译,显示原本的英文。如果强行让他搞的话,游戏会在显示相关文字的时候直接崩溃。虽说也应该能让大肥鱼自己扩充字库,但出于性价比的考虑(设置菜单毕竟也不是经常翻的,而且没被翻译的部分基本都不会碰它,而且也影响不到游戏),所以说就直接让大肥鱼就这样将计就计了。
大肥鱼正在深度思考DeepSeek
已完成思考以下内容由 AI 生成
这篇文章记录了一个真实发生过的技术攻坚:在一台国产麒麟(ARM64)台式机上,把 2002 年的《侠盗猎车手:罪恶都市》从「5 帧幻灯片」救回到「1280×720 全程 60~88 帧」。
先看结论
国产麒麟 V10 ARM64 机器(华为擎云,GPU 是华为 Maleoon 920)上,GTA 罪恶都市靠开源重制引擎 reVC 跑到了 1280×720 / 57~88 FPS 的硬件加速。
整个故事里真正的难点只有一个:这颗 GPU 没有原生 OpenGL 驱动,只有 Vulkan。所以游戏必须通过一个叫 zink 的「翻译层」——把 OpenGL 命令实时翻译成 Vulkan 再交给 GPU。而麒麟的 Mesa 默认并不给陌生程序开 zink。捅破这层窗户纸之后,你会发现这其实是一条通用套路:老 Direct3D / OpenGL 游戏,在这类国产 ARM 机器上,本质都是「白名单 + 翻译层」两件事。
一、这台机器长什么样
| 项 | 值 |
|---|---|
| 系统 | 银河麒麟 V10 SP1,aarch64(ARM64) |
| 桌面 | UKUI + KWin Wayland(应用实际跑在 XWayland 上,问题更少) |
| GPU | 华为 Maleoon 920(内核 DRM 驱动 hisi-dpu-drm) |
| 关键系统包 | hw-maleoon-920、libzink-hisi、libgl1-mesa-dri 20.0.8-0kylin3k26.1 |
| 游戏 | GTA Vice City 原版数据 + reVC(re3 项目的 VC 重制引擎,开源 C++) |
如果你手头的机器也是「麒麟 ARM + 华为擎云」,那这篇基本可以直接照搬;如果是别的国产 ARM 机型,思路一样,只是驱动包名可能不同。
二、先别急着动手:瓶颈到底在哪
接手这类任务时,我最大的一个教训是——第一步不是动手改,而是先判断性能瓶颈卡在渲染链路的哪一段。否则容易像我一样,在 FSR 上白白绕一大圈。
从游戏到 GPU,渲染只有三种可能:
- 软渲染 llvmpipe——纯 CPU 光栅化,老游戏最常见的「能跑但个位数帧」
- zink 硬件翻译——OpenGL → Vulkan,真正走 GPU(这次的目标)
- 原生 OpenGL 驱动——抱歉,这颗 GPU 根本没有
怎么判断自己在哪一种?看 renderer 字段就够了,一条命令或一行日志:
renderer=llvmpipe (LLVM x.x, 128 bits) → 软渲染,问题在「根本没用到 GPU」
renderer=zink (Maleoon 920) → 硬解正常,成功这台机器默认就是 llvmpipe,罪恶都市只有 5~7 FPS。所以问题根本不是「游戏太重」,而是「GPU 完全闲置」。
三、核心机制:为什么默认是软渲染
麒麟在这类 ARM 机器上的 Mesa 布局,是一个经典的「双栈陷阱」,整件事的根子就在这:
- 系统自带的 Mesa 是 20.0.8(
libgl1-mesa-dri等)。这个版本不认识 Maleoon 920,只能给你 llvmpipe 软渲染。 - 华为另外提供了一个
libzink-hisi包,里面是一套 Mesa 22.3.7,自带 zink 翻译器——能把 OpenGL 实时翻成 Vulkan 交给 Maleoon 920。 - 但 Mesa 不会默认给所有程序都开 zink,而是通过 driconf 应用白名单(
/usr/share/drirc.d/01-hisi.conf),按可执行文件名逐个放行。
这就解释了一个特别迷惑人的现象:glxgears 能硬解(它被写进了白名单),而你自己编译的 reVC 却只能软渲染——差别仅仅在于「可执行文件名在不在白名单里」。
把这一层想通,问题就从「玄学」变成了「白名单工程」。
下面这张图是整台机器上 OpenGL 到 GPU 的真实链路:
flowchart LR
A["游戏 reVC<br/>(发出 OpenGL 3.2 调用)"] --> B{"Mesa 检查 driconf 白名单<br/>(按可执行文件名)"}
B -->|"名字不在白名单"| C["llvmpipe 软渲染<br/>纯 CPU · 5~7 FPS"]
B -->|"名字在白名单<br/>dri_driver=zink"| D["zink 翻译层<br/>(Mesa 22.3.7, libzink-hisi)"]
D --> E["翻译成 Vulkan"]
E --> F["Maleoon 920 GPU<br/>硬解 · 60~88 FPS"]翻译层有一点点额外开销,但从实测看,在这颗 GPU 上完全不是瓶颈。
四、三步解法(可复制的通用套路)
第一步:让依赖栈保持干净
reVC 是动态链接的,运行时会 dlopen 系统里的 libGL。这一步最重要的教训是:
别让 LD_LIBRARY_PATH 指到旧的 Mesa 栈目录。我一开始图省事,把启动器的 LD_LIBRARY_PATH 指到了某个旧依赖目录(里面带 Mesa 20.0.8),结果就算装了 zink 也照样软渲染——因为加载器优先命中了旧栈。正确做法是:把游戏所需的非 GL 依赖(比如 libglfw.so.3)就地复制进游戏目录,让 LD_LIBRARY_PATH 只指向游戏目录本身,GL 部分交给系统按白名单去选。
# 验证依赖无缺失(在设置了 LD_LIBRARY_PATH 的前提下)
LD_LIBRARY_PATH="$G" ldd "$G/reVC" | grep "not found" # 应该没有输出第二步:把可执行文件名加进 zink 白名单
最干净、最通用的做法是写用户级配置 ~/.drirc——不需要 root,也不碰系统文件:
<?xml version="1.0" standalone="yes"?>
<driconf>
<device>
<application name="reVC" executable="reVC">
<option name="vblank_mode" value="0"/>
<option name="dri_driver" value="zink"/>
</application>
</device>
</driconf>三个要点:
executable匹配的是可执行文件名,不是路径——所以一旦重编译改了名字,就要重新放行一次dri_driver=zink让 Mesa 强制走 zink;vblank_mode=0把垂直同步的控制权交还给游戏- 一个
<device>里可以塞多个<application>(我就同时放了reVC和java,后者给 Minecraft 用)
第三步:验证 renderer 已经切换成功
改完白名单重启游戏,看日志开头的 GL 信息:
[GLINFO] renderer=zink (Maleoon 920) | version=3.2 (Compatibility Profile) Mesa 22.3.7 | vendor=Collabora Ltd只要 renderer=zink (Maleoon 920) 一出现,就说明 OpenGL 已经被翻译成 Vulkan、真正交给 GPU 了。这一瞬间,帧率从 5~7 直接跳到 60~90。
五、一个更值钱的思路:先测「性能天花板」
在纠结要不要上 FSR、要不要降分辨率之前,先关掉帧限制,测一下引擎的真实上限。这一步能帮你省下大量无效投入。
reVC 有个 FrameLimiter=1,默认把帧率锁在 30。我一开始看到的「30 FPS」,其实是这个限制,不是硬件瓶颈。把 FrameLimiter=0 后实测:
| 场景 | 800×600 | 1280×720 |
|---|---|---|
| 过场动画 | 80 | 73 |
| 街景 | 62~67 | 77 |
| 驾驶 | 67~88 | 57~80 |
结论一目了然:瓶颈在帧限制,不在填充率。所以正确方向是「提高分辨率」,而不是「FSR 把内部分辨率降下去再补回来」。我实测开 FSR 1.0 反而掉到 28~29 FPS——多出来的翻译和上采样开销得不偿失。
这条教训值得反复记住:先测上限,再谈优化。 很多人(包括一开始的我)一看帧率低就上 FSR、降分辨率,结果发现瓶颈压根不在渲染负载上。
六、FSR 为什么只能选 1.0
如果你确实需要超分辨率,那版本的选择几乎是被锁死的:
- FSR 1.0:纯后处理(EASU 上采样 + RCAS 锐化),只需要颜色缓冲——老游戏都能用
- FSR 2/3:需要深度缓冲 + 运动矢量 + 亚像素抖动,这些是 2002 年的 RenderWare 管线根本不提供的东西,硬塞等于重写整个渲染器
所以老游戏上 FSR 的答案固定是 1.0,而且前提是「填充率真的不够」,否则没有任何收益。
七、一路踩过的坑(症状 → 根因 → 解法)
这些是我在真机上一个个撞出来的,整理成表,方便你对照自查:
| 症状 | 根因 | 解法 |
|---|---|---|
| 帧率个位数 | 走了 llvmpipe 软渲染 | 见第四节的「白名单 + 干净依赖栈」 |
启动报 Cannot find desired video mode 闪退 | 远程桌面(如向日葵)接管后,XWayland 只暴露当前分辨率一种模式,游戏找不到就退出 | 选模式处加兜底:窗口模式下找不到匹配的独占模式,就直接用窗口模式 + 配置里的尺寸 |
| 屏幕设置页 SIGSEGV 崩溃 | 上一个兜底让「当前分辨率」索引指到了窗口模式条目,而该条目被填成了空指针,绘制文本时解引用崩溃 | 给窗口模式也生成描述文本。一个列表若有多处消费点,改一处必须查另一处 |
| 游戏里角色一直往左走 | 远程软件注入了 keydown 却没发 keyup,X 层按键卡死(用 XQueryKeymap 能直接看到) | 断开远程后 xdotool keyup <键> 释放;这是远程软件的问题,不是游戏 bug |
| 输入法吃掉 WASD | 中文输入法在游戏里拦截按键 | 在焦点回调里用 fcitx-remote 检测并切成英文直通,失焦再恢复 |
| 鼠标被锁在窗口里出不去 | GLFW_CURSOR_DISABLED 抓取了指针 | 加「Alt 临时释放 + 失焦释放 + 点回收回」的焦点回调 |
八、写给想复现的人:一条可迁移的方法论
把这套经验抽象成六步,几乎适用于所有「国产 ARM 机器上跑老 PC 游戏 / 老 OpenGL 程序」的场景——包括 Minecraft、各类 re3/reVC 重制引擎,甚至其他依赖 OpenGL 的桌面软件:
- 先判瓶颈 ——
glxinfo -B或日志里的 renderer 字段,确认是 llvmpipe 还是 GPU - 理解双栈 —— 麒麟 ARM 机器上「系统 Mesa 太老 + 华为单独给 zink Mesa」,版本差异就是软/硬解的开关
- 白名单放行 —— 把可执行文件名写进 driconf(
~/.drirc或系统drirc.d),设dri_driver=zink - 保依赖干净 ——
LD_LIBRARY_PATH别指向带旧 Mesa 的目录,非 GL 依赖就地放 - 先测上限 —— 关帧限制 / 垂直同步,确认真实瓶颈,再决定要不要 FSR 或降分辨率
- 验证闭环 —— 每次改动后看 renderer 字段 + 实际帧率,别凭感觉
给读者的自查清单(读完就能动手)
- [ ] 你的机器是麒麟 ARM + 华为擎云(Maleoon 920)吗?不是的话,思路通用但包名要自己查
- [ ] 先跑
glxinfo -B,看当前是llvmpipe还是zink - [ ] 确认
libzink-hisi已安装 - [ ] 写
~/.drirc白名单(可执行文件名 +dri_driver=zink) - [ ] 检查
LD_LIBRARY_PATH没有指向带旧 Mesa 的目录 - [ ] 重启游戏,看日志 renderer 是否变成
zink (Maleoon 920) - [ ] 关掉
FrameLimiter,先测真实上限,再决定要不要优化