Air 系列应用交流/轻翼|AirWing · 鸿蒙 PC 适配全记录

轻翼|AirWing · 鸿蒙 PC 适配全记录

【Air 系列 · 鸿蒙 PC 适配全记录】54 / 56

■ 应用:轻翼(AirWing)
■ 定位:主机游戏模拟器(Strato)
■ 上游:Strato | 路线:引擎直连 Vulkan | 版本:1.0.0
■ 市场状态:开发中,即将上架
■ 官网(免费下载): https://www.shunidc.cn/

————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/

PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper

适配开源地址:(AtomGit 仓库建立后回填)

环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743

▍一、为什么要适配轻翼(AirWing)

Switch 模拟器是桌面平台上最吃系统底层的应用品类之一:它要在用户态重建一台主机的 CPU、GPU、音频与操作系统服务。对鸿蒙 PC 来说,这类应用的价值不只是多一个娱乐入口——模拟器对内存映射、可执行内存权限、Vulkan 表面、线程调度和信号机制的要求极端苛刻,把它跑起来等于把鸿蒙用户态的底细摸了一遍。

轻翼(AirWing)就是这个品类的鸿蒙移植工程,当前版本 1.0.0,包名 com.airwing.app,deviceTypes 覆盖 phone、tablet、2in1,Ability 锁横屏,运行库目标 ABI arm64-v8a。它有一段大多数移植项目不会经历的历史:前代方案(Sudachi/yuzu 自研适配,v1-v131)在鸿蒙上被实证走不通,2026-08-17 整个 CPU 执行层推倒重来,换成 Strato(skyline 后继)路线。工程 PORTING.md 完整保留了这次"四选一"的决策记录,ai_log/ 从 v133 起记到 v440,两百多份会话记录串起了从交叉编译通过到商业游戏在真机进入关卡内玩法的全过程。

▍二、先划清边界:不把编译通过当成适配完成

模拟器移植的陷阱在于"能编译"与"能玩"之间隔着整个执行架构。前代 v1-v131 已经证明:把 yuzu 系内核的代码全部编成 .so,Vulkan 管线也能点亮 homebrew,但商业游戏永远卡在 CPU 执行层。因此 AirWing2 立项时先划的边界是执行架构边界,而不是功能清单边界。

层次 传统桌面方案(yuzu/Sudachi 桌面版) 鸿蒙侧当前方案
CPU 执行 动态翻译 JIT + fastmem(依赖连续 512GB 宿主 VA 镜像) Strato NCE:guest ARM64 代码原生执行,guest 区域按原地址稀疏映射
系统调用模拟 宿主内自建 SVC 处理 代码段内 SVC 打桩 → SIGTRAP 陷捕进内核模拟
前端界面 Qt 桌面前端 ArkTS 壳:SetupWizard / GameEmulator / 各设置页共 20 余个页面
GPU 呈现 Vulkan 桌面 surface VK_OHOS_surface + OHNativeWindow,经 XComponent 上屏
音频 SDL/AAudio OHAudio(HarmonyAudio.cpp 通道)
文件与 ROM 管理 桌面目录直接访问 系统 Picker 导入,ROM 目录持久化密钥/存档/着色器缓存
日志体系 控制台输出 文件日志优先、仅 ERROR 上 hilog(防流控黑屏假活)
可执行内存 桌面进程默认可用 kernel ALLOW_WRITABLE_CODE_MEMORY 权限 + 核心 .so 权限清单

这份表里最关键的第一行是整个项目的生死线:鸿蒙进程虚拟地址空间降级后 arena 仅约 8.5GB,39 位 guest 映射被截断,yuzu 的 fastmem 方案直接失效,JIT 寻址踩未映射内存触发 SIGILL/SIGSEGV;而前代始终跑在解释器/降级 JIT 模式,商业游戏加载在纯解释速度下永远完不成。渲染从来不是问题——v131 用 FAKE-08.nro 实测 Vulkan 管线 45000+ DrawCall 正常出画。死点唯一集中在 CPU 执行策略与内存模型,这就是推倒重来的全部理由。

▍三、鸿蒙版的整体架构

移植沿用 Air 系列双层架构:ArkTS 壳承担全部用户交互与系统能力,通过一个只链系统库的 NAPI 薄壳连接核心;重型模拟核心 libstrato_core.so 由 OHOS NDK 独立交叉编译,从 Android 原生设计迁移而来。

text
ArkTS 壳(entry/src/main/ets)
│ SetupWizard 密钥导入(prod.keys)/ GameFolders ROM 目录 / GameEmulator 虚拟手柄与键鼠
│ XComponent(SURFACE) + setXComponentSurfaceRect / PerfStatsOverlay 性能统计
│ NAPI:libentry.so(entry/src/main/cpp/napi_init.cpp,薄壳只链系统库)
▼
libstrato_core.so(Strato skyline 核心,OHOS NDK 交叉编译,native/scripts/build_strato.sh)
├── NCE:guest VA 原生执行 + SVC 陷捕(m0/nce_selftest.cpp 为最小验证程序)
├── GPU:Vulkan → VK_OHOS_surface / OHNativeWindow
├── HLE OS 服务:nvdrv/gpu/vi/audio/hid/bsd…
├── FMV 视频:NVDEC/VIC 通道(ffmpeg 7.1.1 预构建接入,v297)
└── 平台替换层:JNI→NAPI、AAudio→OHAudio、ashmem→mmap/memfd、alog→文件日志+hilog


主要目录如下:

text
AirWing/
├── PORTING.md # 选型决策、架构图、依赖矩阵、风险登记册、里程碑
├── ai_log/ # v133 起全程工作记录与真机取证证据目录
├── native/
│ ├── sources/strato/ # Strato 上游源码(ARM64 Android 原生设计)
│ ├── sources/ffmpeg-7.1.1/ # FMV 解码依赖源码
│ ├── scripts/build_strato.sh # 核心独立交叉构建(debug/reldeb/release)
│ ├── scripts/strato_core_permissions.json # 核心 .so 权限清单
│ ├── prebuilt/ffmpeg/ # ffmpeg OHOS 预构建库
│ └── patches/ # OHOS 适配补丁
├── scripts/ # 真机取证辅助脚本
└── entry/src/main/
├── ets/
│ ├── pages/ # Index / SetupWizard / GameEmulator / 图形·音频·输入·系统设置等
│ ├── services/ # EmulatorService / GameService / InputService / SettingsService
│ ├── models/ # Game / GameDir / Driver / Patch / SettingsModel 等
│ └── components/ # GameCard / PerfStatsOverlay
├── cpp/
│ ├── napi_init.cpp # libentry.so:模拟生命周期/配置/输入三组 NAPI 接口
│ └── m0/nce_selftest.cpp # M0 NCE 最小验证(SVC 陷捕/W^X/tpidr_el0)
└── libs/arm64-v8a/ # libstrato_core.so 与 ffmpeg 运行时部署目标


▍四、核心适配过程

### 1. 选型:四案对比与 NCE 的绕行逻辑

PORTING.md 第 0 节记录了完整的四选一:Ryubing(Ryujinx 后继)因 C#/.NET 平台层被排除——鸿蒙 musl/OHOS 上没有任何 .NET 运行时,等于要先移植 CLR;Skyline 2023 年停更且代码是 Strato 的严格子集;yuzu 系就是前代路线,v1-v131 实证不通。Strato 入选的理由有三:2026 年仍在更新、ARM64 Android 原生设计与 OHOS NDK 同构度最高、NCE 硬编码架构。NCE 不做指令翻译——guest 代码就是 host 代码(同为 ARM64),原生速度执行;不需要 fastmem 那种巨型连续空洞,guest 区域按原地址稀疏映射(MAP_NORESERVE);SVC 用 SIGTRAP 陷捕,内存权限陷阱用 mprotect + SIGSEGV/ucontext,musl 对 SA_SIGINFO/ucontext 完整支持;bionic 与 musl 都以 tpidr_el0 持有 host TLS,skyline 的换手方案对两者正交。

### 2. M0:一票否决的最小验证

选型再漂亮也要真机说话。工程把"NCE 信号/映射在 OHOS 的可行性"登记为最高风险 R1,并设计了对成败一票否决的 M0:entry/src/main/cpp/m0/nce_selftest.cpp 实现最小验证程序,覆盖 SVC 打桩 + SIGTRAP/ucontext + W^X 切换 + tpidr_el0 换手四个死点。2026-08-18 双机 PASS,路线才被最终确认;次日 libstrato_core.so 全量交叉编译通过(9.3MB,13 个 C ABI,NEEDED 全部为 OHOS 系统库)。EmulatorService.ets 至今保留 runNceSelfTest() 接口,把这次验证固化成可随时复跑的诊断入口。

### 3. Android 依赖面逐一替换

PORTING.md 的依赖矩阵逐项列出了 Android 专有 API 的鸿蒙替换:JNI 换 NAPI 加 napi_threadsafe_function;ANativeWindow/vkCreateAndroidSurfaceKHR 换 OHNativeWindow/vkCreateSurfaceOHOS;AAudio/oboe 换 OHAudio;AAssetManager 换 rawfile 首启沙箱部署;ashmem 换匿名 mmap/memfd;__android_log_print 换文件日志加 hilog;pthread_cancel 等 bionic 特有调用以 no-op stub 处理。音频后端、logger 非阻塞、宿主大栈、逐游戏配置等运行期项在 v154 一类会话中逐个落定。这条矩阵的价值在于编译期暴露:Android API 面一旦替换完毕,后续问题就只剩真机行为问题。

### 4. 渲染、输入与游戏链路落地

呈现链上,GameEmulator.ets 用 SURFACE 型 XComponent 承载 Vulkan 输出,并显式调用 setXComponentSurfaceRect——不调用会导致 OHNativeWindow buffer 不被合成器显示而黑屏(v96 记录)。输入层由 InputService.ets 与 NAPI 的 setButtonState/setAnalogState/setTouchState 承接,GameEmulator 内以覆盖层按钮配置实现虚拟手柄,另注册键鼠通道;视频播放走 NVDEC/VIC 通道,v437 会话实测 1920x1080 VP9 连续解码 11400+ 帧、4 次流切换检测正确。密钥与固件经 SetupWizard.ets 的系统 Picker 导入,keys/save/着色器缓存持久化在 ROM 目录(filesDir 卸载即丢,故改外部持久位置)。

### 5. 商业游戏逐个收敛

ai_log 记录了游戏兼容性的真实收敛过程:BotW 经历毕昇着色器毒物、SSBO 装载单指令毒物等连环定性后从 27fps 首渲走到复活可玩;织梦岛打通全流程进游戏世界;节奏天国从死点到标题页、剧情、通关;v437 会话 Kirby 首次全链贯通到关卡内(含历史上两次阻塞的上船/warp 域),Zelda 稳定运行 13 分钟零退出。这些记录同时留下完整的法医方法:三轮 boot 回归、文件级 git stash 二分、探针活性验证、RSS 采样,每一步结论都有日志与截图证据目录背书。

▍五、适配中遇到的几个难点

### 难点一:fastmem 的 512GB 幻觉

yuzu 的 fastmem 需要连续 512GB 宿主虚拟地址镜像 guest 39 位空间,桌面 64 位环境理所当然,鸿蒙进程 VA 降级后 arena 仅约 8.5GB(0x200200000),39 位映射被截断。前代 v123-v125 的 SIGILL/SIGSEGV 全部源于 JIT 直接寻址踩未映射内存。解法不在修补而在绕行:换用 NCE 后 guest 代码原生执行,不再需要为翻译后代码维护巨型地址镜像,只需零散空洞的稀疏映射。这是"换架构而不是调参数"的典型案例。

### 难点二:NCE 从未启用导致解释器死局

前代另一个隐性问题更隐蔽:由于 NCE 通道在鸿蒙上从未打通,内核始终运行在解释器/降级 JIT 模式。解释器下商业游戏的加载时间以不可接受的方式膨胀(v130 结论"游戏加载时间过长"),且这种慢不会崩溃、不会报错,只会让人怀疑一切。工程用 v131 的 FAKE-08.nro 实验先把渲染从嫌疑名单中摘除(45000+ DrawCall 正常出画),把死点锁定到 CPU 执行层,才让推倒重来的决策有了证据基础。

### 难点三:回归修复中的承载性行为

v437 会话记录了一个教科书级教训:修复 Fence 语义(快路径返回值 false 改 true)后,新构建引导期 12 秒楔死。二分四轮构建才定位真凶——旧返回值 false 是 host1x WAIT 处理里"强制补值块"的隐式触发器,而那段补值顺带把 guest 镜像同步到阈值,是承载性行为;改掉返回值等于暗中删掉一次同步,游戏侧永远差一个 increment。修复方式是让 WAIT 处理无条件补齐 guest 镜像。同一会话还揪出第二雷:GetBacking 的自旋锁与 SynchronizeHost 持锁等 Vulkan fence 构成循环等待,解法是给 backing 生命周期独立叶锁,锁内不允许任何长等待。方法论沉淀进了 PORTING 的对策表:改"看起来是 bug"的返回语义前,先 grep 全部调用方做行为等价性分析。

### 难点四:系统级内存杀手的法医定位

2026-09-29 的 v440 分析闸把一个长期悬案定谳:二次上船场景 15 秒内 RSS 从 4.4GB 涨到 11.19GB,随后进程被系统 RSS_THRESHOLD_KILLER 前台 SIGKILL。死亡路径完全重建——Vulkan HOST_VISIBLE 常驻映射落入 dma-heap→shmem 桶计入 RSS、mremap 别名复制造成物理页双计、bufferMappings 只增不减导致旧场景 backing 滞留,三个通道叠加出 +6.5GB 的爆涨;全套进程内退出探针零命中反过来证明 SIGKILL 不可捕获。这解释了历史上"无 crash dump、faultlogger 空"的悬案群。修复候选按风险升序登记(裁剪参数下调、场景边界触发 trim、按脏区间触页),尚未定案——工程对未修复问题的态度是摆证据而不是宣布胜利。

### 难点五:打包五件套从第一天配齐

巨型 .so 在鸿蒙上有专属的坑:musl 严格 dlopen 会让闭包外的组件静默丢失,hvigor 默认 strip 与压缩都可能破坏 ELF。工程按 Air 系列经验在第一天配齐五件套:strip:false、compressNativeLibs:false、entry/libs 模块级整目录通道、拓扑序 dlopen、ELF 原位编辑工具;核心权限经 native/scripts/strato_core_permissions.json 声明 ALLOW_WRITABLE_CODE_MEMORY 与 INHERIT_PARENT_PERMISSION,由安装系统合并授权。这些选择让 libstrato_core.so(含 ffmpeg 视频通道)在多轮迭代中保持"改完即装、装上即跑"的节奏。

▍六、编译、安装与启动

构建分两段:核心库独立交叉编译,HAP 走标准 hvigor 流程。native/scripts/build_strato.sh 自动定位 OHOS NDK(命令行工具优先,DevEco 内置次之),以 ohos.toolchain.cmake 配置 arm64-v8a 交叉编译,产物直接部署到 entry/libs/arm64-v8a/:

bash

native/scripts/build_strato.sh reldeb


export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk

hvigorw --mode module -p module=entry assembleHap --no-daemon


签名产物位于 entry/build/default/outputs/default/entry-default-signed.hap。连接设备后安装并启动:

bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airwing.app


首次使用按 SetupWizard 引导导入 prod.keys 密钥并配置 ROM 目录;runNceSelfTest() 可随时验证当前设备的 NCE 通道健康状态。

▍七、当前功能边界

已覆盖的能力(均以 PORTING/ai_log 记录为据):

- NCE 执行架构真机验证(M0 双机 PASS)与 libstrato_core.so 全量交叉编译(M1,9.3MB、13 个 C ABI);
- 商业游戏真机运行:BotW 复活可玩、织梦岛全流程进游戏世界、Kirby 全链贯通到关卡内、Zelda 13 分钟稳定、节奏天国剧情通关、SuperHaxagon 等 homebrew;
- 1080p VP9 硬件通路视频播放(NVDEC/VIC + ffmpeg 7.1.1),11400+ 帧连续解码实证;
- 密钥导入、ROM 管理、虚拟手柄与键鼠输入、OHAudio 音频、性能统计浮层、逐游戏配置;
- 三形态设备部署实证:MateBook Pro(2in1)、MatePad Mini、MatePad 11.5。

尚未覆盖或有据可查的开放项:Kirby 关卡内黑屏(判决变量定位到 FS c6 级联阴影链,需再加一轮 dump 探针)、warp 场景挂死(维持 guest 谓词等待定性,P0 对照实验因素材缺失暂阻)、二次上船的 shmem 爆涨修复(四个候选按风险升序待定夺)、卡比关卡进入期 ArkTS/WMS 窗口失联(strato 侧健康,方向在壳层窗口生命周期)、存档落盘位置定位。PORTING.md 的里程碑登记停在 M1,M2 之后的目标(homebrew 出画、商业游戏加载、输入音频打磨)实际由 ai_log v133-v440 的会话记录逐项兑现,文档登记与工程进展的同步本身也是登记在案的待办。

因此,当前版本更准确的定位是"以 NCE 原生执行为地基的 Switch 模拟器可玩版":执行架构已经过最大风险验证并支撑起真实游戏,兼容性与内存行为仍在以法医式节奏逐案收敛。

▍八、总结

轻翼的适配史在鸿蒙 PC 生态里有独特的参考价值:它不是一条从成功走向成功的直线,而是先用 131 个版本证明一条路不通,再以完整的选型对比、一票否决式最小验证和两百多份证据化会话记录,把模拟器这个最苛刻的应用品类带到真机可玩。NCE 路线绕开 fastmem 与解释器两大死点的过程,也顺带为鸿蒙平台沉淀了用户态内存映射、信号陷捕、W^X 权限、Vulkan 表面等一整套实证结论,这些结论已经通过依赖矩阵的形式反哺同系列工程。

后续工作重心清晰且全部有案可查:按风险升序处置 shmem 爆涨与 warp 挂死两案,用 c6 dump 轮推进卡比黑屏的着色器级修复,补齐存档定位等外围验证,并把 PORTING.md 的里程碑登记追平实际进度。这个工程的验收标准从来不是构建通过,而是骰子账本上的通过率、RSS 曲线的平稳度和真机关卡内的实际玩法——这也是它对整个移植方法论最大的贡献。
楼主 8 天前
想参与讨论?登录 或 30 秒注册。