这是本节的多页打印视图。 .
手册
- 1: AI 诊断手册
- 2: Android 公开候选验证:LAS Recomp
- 3: Graphics 契约:NVN 到 Metal/Vulkan
- 4: LASRecomp Android:从成熟项目提炼的可复用架构
- 5: Runtime 契约:loader、HLE、内存和线程
- 6: 本地案例:关键突破是如何发生的
- 7: 从调查到验收的工作流
- 8: 对游戏开发的长期价值
- 9: 发布前审计记录
- 10: 公开发布前清单
- 11: 贡献、引用和许可证
- 12: 关键突破复盘:从黑屏到可玩的路径
- 13: 核心模型与原则
- 14: 可复用工具与证据产物
- 15: 来源登记表
- 16: 来源与公开范围
- 17: 目标架构:可复用的静态重编译平台
- 18: 目标完成矩阵
- 19: 平台差异:为什么 Mac 快、Android 难
- 20: 契约重建研究方向
- 21: 实验与证据模板
- 22: 外部项目解读
- 23: 游戏特化 Profile
- 24: 游戏选型与架构成熟度阶梯
1 - AI 诊断手册
AI 的首要任务是缩小契约候选空间和组织证据,不是凭截图自由生成图形代码。优先使用结构、数值和时序 oracle;视觉 模型只作为低成本筛查器。契约分类、等价类和验证层次见 21-contract-reconstruction-research.md。
接到一个新症状时
1. 先分类,不猜修复
把症状标为:启动/ABI、服务、内存、线程、文件、输入、shader、纹理、target/composite、present、性能或内容缺失。 同一现象可以有多个候选,但只能选择一个最小下一证据。
2. 写出不变量
例:
- “黑屏”不变量:至少一个有效 draw 写入最终可达的 target,且 present source 在 acquire 后包含该 target 的内容。
- “文字缺失”不变量:VS/FS pair、glyph vertex/UV、atlas view、descriptor、discard 输入和最终颜色都可解释。
- “139”不变量:fault address 属于哪个 guest region,调用者是否检查了分配失败,宿主是否越过了边界。
3. 选不扰动时序的证据
优先:静态反汇编、离线 shader reflection、文件 hash、低频计数、单次 snapshot、同 draw 的结构摘要。 最后才是高频日志、强制 sleep、全量 dump 和改变调度的开关。
4. 只做一个实验
实验必须回答一个二分问题:
实验结束后马上记录结果、清理现场、更新“已排除列表”,不要把未命中误写成“系统正常”。
5. 用参照系收敛
优先选择:Mac 同场景、另一个已知可工作的 title、离线三角形、纯色 shader、golden texture、原始 guest bytes。 参照系必须记录版本和输入;否则“Mac 正常”也可能只是不同存档或不同资源路径。
决策树
AI 输出格式
每次给出:
- 当前已证实事实;
- 已排除事实;
- 最多三个候选,按证据排序;
- 一个最小实验或静态检查;
- 预期的三种结果和下一步;
- 回滚/清理动作;
- 文档条目编号和是否需要 commit。
禁止只输出“可能是竞态”“可能是 patch”而没有可证伪的下一步。
2 - Android 公开候选验证:LAS Recomp
核对日期:2026-10-24
候选
Nycolazs/LASRecomp-Android-Plus 是《塞尔达传说:梦见岛》
Switch 版的 ARM64 Android 静态重编译移植。公开 README 说明它提供不含游戏内容的 APK,运行时需要用户自己的
游戏文件;它支持 .xci/.nsp 或已解包的 ExeFS/RomFS,并需要 prod.keys。它的运行时包含 Android/Vulkan、
触摸输入和手机端构建流程。
本机验证
| 项目 | 结果 |
|---|---|
| 目标设备 | 当前唯一 ADB 设备 PYB110 |
| Android | 17 |
| ABI | arm64-v8a |
| APK | LASRecomp-Android-1.2.0.apk,发布页 SHA256 校验通过 |
| 安装 | 成功 |
| 启动 | 成功进入 Game Files 页面,Vulkan 初始化日志正常 |
| 进入游戏 | 已进入标题画面并播放开场电影;触控摇杆、A/B 键可见 |
这次验证已经覆盖 Android shell、APK 安装、ARM64 加载、文件导入、手机端解包/重编译、Vulkan 出帧和游戏输入。
应用在设置完成后显示 Game files ready、Nintendo Switch · v1.0.0;虽然输入的是本地保存的 v1.0.1 XCI,
该候选的运行时使用其支持的基础游戏版本显示名。标题画面、开场电影和触控覆盖层均正常出现。
本机资料盘点
当前已找到的 Switch 镜像:
aa456.xci:Apollo Justice 456 Trilogy;不是本候选需要的游戏。xc3.xci:Xenoblade Chronicles 3;不是本候选需要的游戏。- AA123、AAI 和 Cuphead 的本地资料;都不是梦见岛。
- SMB
sharetome挂载中找到梦见岛 v1.0.1 和 v1.1.0;本次使用 v1.0.1。
本次把 v1.0.1 XCI 复制到 /Volumes/Untitled/lasrecomp-link-awakening-v1.0.1/,再通过 ADB 放入当前设备的
Download 目录;prod.keys 只作为运行时输入,没有进入仓库或文档内容。SMB 来源保持不变。
可复用排障经验
- 先安装公开 APK,验证 package、ABI、Activity、Surface/Vulkan 和初始日志;这一步不需要游戏本体。
- 再核对候选项目要求的 title/version/更新包,不要把“同为 Switch”当作兼容。
- 最后才导入用户自己的合法游戏资料并开始手机端重编译;首次构建的空间、温度和时间必须记录。
- 如果缺本体,结论写成“shell 验证完成、游戏验证阻塞”,不要改用不匹配的镜像试跑。
2026-10-24 真机结果
- 当前唯一设备:PYB110,Android 17,ARM64;本轮没有连接或操作其他手机。
- XCI 大小为约 6.31 GB;文件选择器最初不显示
.keys,复制为keys.txt后通过搜索选择,应用接受了同一份 keys 内容。这个绕过只发生在设备临时目录,不是项目代码修复。 - 手机端重编译分为读取游戏代码和重新编译两个阶段,界面显示约 4 分钟完成;设备剩余空间约 104 GB,未遇到 解密失败、bad keys、崩溃或 Vulkan 建设备错误。
- 标题画面显示《Link’s Awakening》、
Press L + R和虚拟摇杆/A/B 控制;发送 L+R 后进入开场电影。 - 运行日志观察到 Vulkan 约 60 FPS、约 13 ms GPU、0 skipped draws;电影解码线程持续产出帧。此处只记录观测值, 还没有把它当作完整流程性能结论。
当前边界
这证明公开候选在这台 Android 设备上能完成“用户资料 → 手机端静态重编译 → 标题/电影出帧”的链路;它不是 nxnative 的 AA123/AAI 迁移结果。下一步应分别记录:跳过开场电影进入存档、触控输入完整性、长时间稳定性、渲染 性能,以及更新包 v1.1.0 是否能按 README 的支持矩阵复现。游戏资料和密钥仍属于本机运行输入,不应提交到公开手册。
3 - Graphics 契约:NVN 到 Metal/Vulkan
图形移植不只是 API 逐条翻译,而是把 CPU 命令流、shader stage IO、资源元数据、管线状态、同步和宿主能力共同 组成的隐含契约显式化。完整的契约重建研究和未来工具方向见 21-contract-reconstruction-research.md。
图形链路分层
任何一层的“dummy”“空 handle”“旧 snapshot”都可能静默地产生黑、白或透明画面。
先确认 draw 是否存在
按以下顺序查:
- guest 是否发出 draw/dispatch;
- program 是否为非零、VS/FS 是否都存在;
- vertex/index/UBO 是否有效;
- target 是否有 host backing;
- descriptor 是否绑定真实 image/view/sampler;
- shader 是否产生片元;
- target 是否被后续 pass 合成到 present source。
“no program”不能自动解释黑屏:可能是合法的空绑定,也可能是宿主丢失了 guest state。
Shader 迁移
离线翻译要记录:原始 shader hash、stage、control/code、翻译器版本、binding reservation、SPIR-V/Metal 输出 hash。 必须验证 VS outputs 与 FS inputs 的 location、component、类型和插值语义;不能只验证 shader 编译成功。
纹理与通道
逐个资源记录:NVN format、target、levels/layers、stride/layout、pool offset、view swizzle、host format、 alpha 语义、是否 CPU 写入、是否 render target、是否可复用。R8、BC、capture texture 和 render target view 不能共享同一套全局“聪明规则”。
对象生命周期
对象 key 至少要覆盖 storage identity、描述、format、view/swizzle、代际和 dirty/upload 状态。对象被销毁、pool 重注册或 guest 地址复用时,旧 image 不能以“看起来相同”的 key 复活。异步 replay 下必须让 GPU 完成后再退休资源。
Present 和同步
每个 swapchain image 需要独立的 acquire/submit/present 生命周期;不能只用一个全局 semaphore 或假定窗口 image
始终对应 guest 当前 render target。动态渲染、传统 render pass、sync2、timeline semaphore 等能力要根据 API 版本
和扩展查询,不根据 vkGetDeviceProcAddr 返回非空就判定支持。
诊断的正确用法
- 固定颜色:验证 shader/目标是否真的产生片元;不是修复。
- magenta dummy:验证是否走 fallback;不是证明真实资源错。
- snapshot/readback:判断写入发生在哪个 target;注意 snapshot 时机。
- per-draw fingerprint:比较 Mac/Android 有效输入;优先 hash/摘要,避免大量日志改变时序。
4 - LASRecomp Android:从成熟项目提炼的可复用架构
核对日期:2026-10-24
本章分析公开的 LASRecomp 与 LASRecomp-Android-Plus。目标是提炼静态重编译的工程方法, 不是复制游戏代码、地址表或 Android 实现。
先看结论
Android fork 的工作量确实很大,但它建立在上游已经完成的“游戏逆向 + 生成器 + 通用 runtime”之上。两边共同基线 之后的公开历史可以粗略看成:上游约 87 个提交、43 个文件和 3,086 行新增;Android 线约 182 个提交、154 个文件和 30,085 行新增。后一个数字包含合并的上游提交、自动生成的性能资料、第三方依赖、文档和脚本,不能当成纯手写 Android 代码量。
更准确的分工是:
| 层 | 上游主要解决 | Android fork 主要解决 |
|---|---|---|
| 游戏特化 | Switch ARM64 反汇编、build ID、地址表、patch、shader 和资源规则 | 复用上游 profile,并适配版本选择和用户导入 |
| 通用 runtime | CPU/ABI、Horizon HLE、内存、线程、文件、输入、音频、电影、NVN | 补 Android 生命周期、线程和存储边界 |
| 图形 | Metal/Vulkan 资源模型、shader 翻译、NVN 语义 | 移动 Vulkan、Adreno/Turnip、帧生成、缓存和性能 |
| 产品外壳 | 桌面启动器和资源目录 | APK、JNI、SAF、触屏、后台构建、断点续编译 |
因此,移植一个新游戏时最应该复用的是分层和验证流程,而不是把另一个游戏的地址、shader 或对象布局搬过来。
静态重编译的四层模型
这个模型有一个重要边界:游戏代码是静态生成的,但系统服务和图形 API 仍由 runtime 在运行时提供。静态重编译并不 等于“所有东西都离线完成”,而是把最昂贵的 guest CPU 执行提前转换,把不可避免的平台行为集中到可测试的边界。
Android fork 最值得借鉴的实现策略
1. APK 只带工具和 runtime,不带游戏
公开 APK 通过用户选择的 .xci/.nsp、prod.keys 或已解包目录完成 setup。游戏资源、生成的 game library 和
缓存位于应用数据目录,公共 APK 可用检查脚本拒绝包含游戏代码的构建。
这对应一个通用的三期产品边界:
nxnative 以后也应把“游戏本体不进 APK”和“生成后的代码是否随包分发”作为两个独立开关,而不是混在构建脚本里。
2. 把 on-device build 当成可恢复的构建系统
Android fork 在手机上运行生成器、Zig/Clang 和 linker;构建过程有进度、前台服务、wake lock、并行度限制和完成 对象缓存。进程被杀或设备重启后,可以从已完成对象继续,而不是从头再来。
可迁移的不变量:
- 每个生成/编译单元有稳定输入 hash;
- build ID、generator 版本、compiler 版本和 flags 进入 manifest;
- 中间结果写入临时名,完成后原子发布;
- 取消、进程死亡和磁盘不足都留下可诊断状态;
- 生成器和编译器可在宿主机用同一输入做 byte-identical 或语义等价检查。
3. 先定义 Android 地址重定位,而不是临时找固定地址
Android 进程的库、zygote 和 heap 会占用地址空间,不能假设 Switch guest 地址能原样映射。成熟做法是把
GUEST_SHIFT 作为生成器、patch 工具、地址表和 runtime 共同认识的正式参数:
这个机制必须在最小合成 guest、真实模块和异常路径分别验证;不能只在某几个跳板上加偏移。
4. 移动 Vulkan 优化必须建立在正确性基线之上
Android fork 的性能提交包含 shader 预热、pipeline cache、帧生成、动态分辨率、预旋转 swapchain、UBWC/LRZ 和 Adreno 驱动处理。但这些优化都应该有固定场景、GPU/CPU 时间、温度、内存和截图对照。
对 nxnative 的直接规则是:
- 先让普通配置稳定完成标题、菜单、电影、输入和保存;
- 再启用异步提交、upload ring、pipeline 并行和 frame generation;
- 任一性能改动都必须有关闭开关和回归结果;
- “更快地显示错误画面”不算性能成功。
上游与 fork 的工作量如何估算
不要用提交数直接比较难度。建议拆成四个预算:
| 预算 | 主要问题 | 是否能跨游戏复用 |
|---|---|---|
| 逆向预算 | build ID、模块、函数、shader、patch、资源格式 | 低到中;工具可复用,结论通常特化 |
| runtime 预算 | ABI、HLE、内存、线程、服务和 NVN 对象 | 中到高;契约可复用,边界要验证 |
| 平台预算 | Android/Metal/Vulkan/窗口/存储/输入 | 高;平台层通常可复用 |
| 性能预算 | shader、同步、缓存、分辨率、设备调优 | 中;方法可复用,参数依设备和游戏 |
上游最昂贵的是第一、二项:它把一个具体游戏变成了可执行的静态重编译对象。Android fork 最昂贵的是第三、四项: 它把已有对象变成可安装、可导入、可在移动 GPU 上长时间运行的产品。
对 nxnative 的落地清单
每个新游戏至少建立以下目录或记录:
通用 runtime 只接受已经被 profile 描述的契约;无法证明跨游戏通用的修复留在 profile 或 adapter 中。
可通过经验明显加速的部分
- 用最小 guest、三角形和合成 HLE 先验证 loader/runtime/graphics,不拿真实游戏当第一台示波器;
- 先扫描 C/C++ 导出、结构体和函数表,再补实现,避免“C 已有、C++ 缺失”或反过来的盲区;
- 先建立 Mac/Android 同一 draw 的差分,而不是只看最终截图;
- 所有静默 dummy、缺 shader、缺 descriptor、空字体和被丢弃 command list 都在首次出现时记录;
- 将高扰动日志和低扰动计数器分开,竞态问题优先用离线 dump、计数和回放;
- 把“解包、生成、编译、链接、加载、首帧、输入、存档”作为独立验收门;
- 先复用成熟 runtime 的接口边界,再决定是否重写内部实现;
- 公开资料只复用许可证允许的代码,GPL/私有来源只抽象语义和测试不变量。
外部项目的证据边界
本章使用 LASRecomp 的公开 README、Android 构建文档、公开源码结构和发布说明。它证明了“该项目采用了哪些 架构和流程”,不自动证明这些流程适用于 AAi、nxnative 或任意 Android GPU。具体文件、commit、许可证和阅读范围 登记在 10-source-register.md。
5 - Runtime 契约:loader、HLE、内存和线程
Loader / ABI
静态重编译最早的错误通常不是“平台不支持”,而是边界不对:符号绑定到错误模块、relocation 未应用、 结构体 packing 不同、返回值或寄存器没有按 AAPCS64 保存。每个 HLE 入口至少要能回答:
- 参数来自哪些寄存器/栈槽;
- 输出 buffer 的大小、对齐和初始化状态;
- 返回值如何驱动 guest 的下一分支;
- 该调用是否可能保存指针,在异步线程稍后使用。
HLE 服务
“成功返回”不是通用 fallback。若调用者随后读取一个未填的结构体,问题会延迟到完全无关的地址崩溃。 对每个 stub 做调用计数和输出 sentinel 检查;合法的“没有设备/没有 amiibo/没有用户”要返回游戏期待的空状态, 而不是任意零值。
Guest memory / VAMM
需要维护至少四个彼此一致的账本:
- guest 虚拟地址区间及权限;
- host backing 页和页粒度;
SetHeapSize与MapPhysicalMemory的物理资源占用;QueryMemory对外报告的分区和状态。
关键不变量:
扩大 alias 或 heap 只能证明“越过了一个崩溃”,不能证明 guest 的资源限制契约正确。
线程、TLS 和调度
Unity/IL2CPP 会依赖线程暂停、寄存器上下文、线程栈、futex/condvar、核心亲和性和 TLS。尤其注意:
- guest 的 x18/TLS 语义不能直接等同于宿主 pthread TLS;
- 小 guest stack 上执行复杂 host hook 会溢出,应切换到大 host stack;
- 线程 SVC 返回不代表等待状态已完成;
- 诊断日志可能改变竞态,必须使用低频摘要或离线回放。
文件、存档和时间
Android scoped storage、xattr namespace、路径大小写、clone/copy 语义、时间精度和随机数都可能影响游戏逻辑, 且最终表现为“黑屏”。先验证文件路径、大小、hash 和 errno,再去怀疑 Vulkan。
通用修复与游戏修复的边界
通用修复可以进入 runtime:ABI、页表、线程状态机、文件语义、对象生命周期、Vulkan barrier。游戏修复必须进入
profile:地址、模块、build ID、容量、shader hash、特定初始化顺序。若一处修复需要写死某个 main+offset,
默认把它视为 profile 变更,而不是通用修复。
6 - 本地案例:关键突破是如何发生的
Android / AAi
从 139 到持续出帧
早期日志在 present #1/#4 后进入 guest allocator NULL。扩大 heap/alias 并没有直接解决它;真正有价值的是把
MapPhysicalMemory、UnmapPhysicalMemory、SetHeapSize、QueryMemory 和物理 backing 放到同一套账本中,随后
才逐渐暴露 render target、对象代际和上传问题。结论:容量实验只能改变边界,不能替代资源契约。
从黑屏到红色,再到标题主体
固定 fragment 输出红色证明 Vulkan 输出链完整;仿射 VS 证明原始 VS 的位置/常量路径有问题;最终标题主体恢复的 关键是按内容校验动态 UBO 上传,避免 Unity 在固定 scratch 地址重写数据后仍复用旧上传。结论:颜色测试是定位工具, 内容校验才是可泛化修复。
从白条到正常菜单
R8 强制通道映射让 UI 框出现,但不是全局修复。之后 Mac 对照、shader miss 补译和完整 shader cache 证明部分 UI 文字缺失来自 shader 产物不全;未选中菜单项仍出现白条时,问题又收敛到离屏目标/呈现新鲜度,而不是字体 atlas 本身。
输入和启动状态
xattr Linux namespace 错误阻止存档创建,造成启动流程看似卡死;修复后进入免责声明、语言菜单和双选项框。HID receipt/latch 和 replay-thread 修复则解决了输入边沿被多个消费者抢读及闪烁。结论:游戏状态阻塞必须和图形阻塞分开。
当前未完项
开场字幕在 Mac 可见、Android 进入独立 1920×1080 中间目标后不可见;最新证据已排除 BC7、movie Decoder、缺 shader
和几何 compute,集中到动态 c3 颜色数据、离屏 target 和合成。下一步必须做 Mac/Android 同 draw 的 UBO 写入与 target
读回,而不是再次全局改 R8、blend 或 present。
Mac / Apple
Mac 侧的通用经验包括:
- IL2CPP 需要真实 module layout、异常 unwind、地址空间和线程暂停语义;
- 小 guest stack 调用复杂文件/NVN/Metal host hook 会溢出,必须使用大 host stack;
- CPU-written linear texture 必须与 host 采样 image 共享正确 backing;
- render target 的 stencil、同 pass feedback、duplicate attachment、origin 和 texture view 都会产生局部画面错;
- shader translation 成功不等于 vertex input、driver segment、binding 和 pipeline state 正确;
- pipeline 分段提交和预热可以把性能问题从“看似卡顿”变成可测量的 CPU/GPU/compile 时间。
完整失败目录属于本项目内部实验记录;公开手册只保留这里的症状、契约、证据等级和不可推广边界。
7 - 从调查到验收的工作流
阶段 0:建立事实表
先记录:游戏版本和 build ID、模块、SDK、引擎、目标平台、CPU/GPU、OS、页大小、Vulkan/Metal 能力、资源包 版本、patch hash 和 shader chain 版本。没有这张表,后面所有“同一版本”都不可信。
阶段 1:离线调查
- 提取 ExeFS/RomFS 清单,不把大文件复制进代码仓库。
- 统计导入符号、系统调用、NVN slot、线程/内存/文件服务入口。
- 对每个重写 site 做反汇编前后对照,并保留原始 bytes、build ID 和 patch 原因。
- 扫描 C/C++ HLE 导出、结构体大小、函数指针表和 C/C++ ABI 镜像。
- 提取 shader、建立 VS/FS/CS program 对、binding 表和 profile。
阶段 2:最小平台闭环
按顺序验证:
这一步不能用真实游戏替代。真实游戏一旦失败,很难知道是加载器、服务还是图形问题。
阶段 3:真实游戏 bring-up
每轮只改变一个变量,并记录:
| 字段 | 内容 |
|---|---|
| 基线 | commit、profile、patch、shader chain、APK/可执行文件 hash |
| 环境 | 设备、驱动、页大小、资源目录、存档状态 |
| 变量 | 只允许一个代码/配置/环境变化 |
| 观察 | 固定时间或固定 frame 窗口;记录进程状态和截图 |
| 证据 | 日志、读回图、对象快照、反汇编或文件 hash |
| 结论 | 已证实、排除、未定、撤回或阻塞 |
| 清理 | 诊断开关、设备文件、临时资源是否恢复 |
阶段 4:建立 Mac/Android 或参考实现对照
对同一游戏状态,优先比较同一 draw:
- VS/FS hash 和 program pair;
- vertex/index bytes;
- UBO/SSBO 内容;
- texture pool 地址、offset、format、stride、swizzle、incarnation;
- render target、viewport、scissor、blend、depth/stencil;
- submit/acquire/present 的顺序和结果。
不要只比较最终截图。截图只能告诉你结果不同,不能告诉你差异在哪一层。
阶段 5:验收与性能
先通过普通配置验收:无诊断开关、干净存档、可重复路线、关键截图和持续运行时间。之后再测:
- p50/p95/p99 frame time;
- CPU translation、GPU execution、上传和 pipeline compile 分项;
- shader miss、draw completion、present drop;
- 内存峰值和长期增长;
- 分辨率、render scale、异步提交等优化的 A/B。
不应重复的实验方式
- 在高频路径加入大量 printf 或分配,随后把新时序当成原始行为;
- 同时改 heap、alias、shader 和 present;
- 只看
return 0就认为 HLE 正确; - 用一个游戏的固定地址修通用 runtime;
- 让“补大内存”“固定颜色”“关闭深度”成为最终修复;
- 把没有 shader cache、没有存档、没有资源的运行结果误判为图形根因。
8 - 对游戏开发的长期价值
状态:未来架构的扩展方向,尚未宣称当前静态重编译 runtime 已经是完整游戏引擎。
从移植工具到开发基础设施
静态重编译首先解决的是“如何让已有游戏在另一平台运行”。但过程中建立的契约模型、资源账本、shader 反射、 确定性回放和跨平台差分,同样可以前移到游戏开发阶段。
对游戏开发的直接帮助
1. 提前发现跨平台图形错误
在 pipeline 创建或离线构建时检查:
- VS/FS location、type、component 和 interpolation;
- shader binding、descriptor、sampler 和 visibility;
- attachment format、sample count、load/store;
- blend/depth/stencil 与 shader 输出;
- resource state、dirty range、barrier 和 submit 顺序。
这类检查可以把“某台手机上偶尔黑屏”提前变成构建期或开发机上的明确错误。
2. 让 shader 和资源构建增量化
将 shader key、反射结果、pipeline 状态和资源依赖组成构建图,可以只重建受影响的产物,减少大型项目的等待时间, 并让 shader cache 与 runtime 版本、设备能力和资源格式保持可追溯关系。
3. 让 GPU 问题可复现
统一的 ProgramContract/DrawContract trace 应能回答:
这比只保存最终截图更适合排查竞态、资源代际、错误格式和多帧 in-flight 问题。
4. 改善多平台性能工程
统一的设备能力矩阵和固定场景可以比较 Metal、Vulkan、DX12、不同 GPU 和不同驱动的:
- CPU replay 时间;
- shader/pipeline 编译时间;
- upload 和 bandwidth;
- GPU frame time;
- memory peak;
- 温度和长期降频;
- pipeline miss 和 present drop。
这样性能优化不再只针对一台开发机,也不会把“更快但画错”误认为优化成功。
5. 支持确定性回归和检查点重放
固定输入、帧边界检查点和资源/shader cache manifest 可以让团队快速重现:
- 特定设备上的黑屏;
- 某个 shader 变体错色;
- 资源上传竞态;
- 更新后的性能回退;
- 某个场景偶发的 GPU fault。
这不等于完整模拟器 save state,但足以把“从启动重新走十分钟”降低为“从检查点重放到目标 draw”。
需要从静态重编译中抽象出来的组件
要服务游戏开发,未来架构需要把以下组件做成独立于反汇编器的工具:
| 组件 | 开发阶段用途 | 静态重编译阶段用途 |
|---|---|---|
| Contract IR | 描述源码/资产生成的图形契约 | 描述从 trace 恢复的图形契约 |
| Resource ledger | 跟踪资源依赖和生命周期 | 追踪 guest pool、image incarnation、upload |
| Shader reflection | 构建期检查 stage/binding/format | 运行时检查翻译后的 program |
| Draw trace | 调试某个材质和 pass | 对比 Mac/Android/原机行为 |
| Deterministic replay | 自动化场景回归 | 复现重编译后的特定 draw |
| Device matrix | 选择 backend 和质量档位 | 定位平台差异和驱动限制 |
| Build manifest | 追踪 shader/asset/toolchain | 追踪 game library/profile/runtime |
边界和非目标
这套架构不会自动替代游戏引擎,也不会消除所有平台差异。它需要游戏团队仍然负责:
- 设计合理的资源和 shader 契约;
- 选择允许的降级策略;
- 定义视觉和数值正确性标准;
- 维护资产、shader 和工具链版本;
- 审核性能和设备覆盖范围。
它提供的是更强的工程反馈:错误更早出现,差异更容易定位,回归更容易重放,平台扩展不必从截图猜测开始。
对未来架构的要求
如果希望同时服务静态重编译和游戏开发,目标架构应满足:
- Contract IR 不依赖某个游戏或某个宿主 API;
- runtime/frontend/backend 可以单独测试和替换;
- trace、ledger 和 manifest 是机器可读格式;
- 构建期检查和运行期诊断共享同一套事实模型;
- 性能优化始终有 correctness baseline 和关闭路径;
- 新平台只需实现能力适配,不直接修改游戏特化代码;
- 所有外部实现、资产和工具都经过许可证和来源审计。
长期定位
因此,未来架构的价值不只是“更快做出下一款静态重编译游戏”,还包括:
- 降低跨平台开发门槛;
- 提高 shader/resource pipeline 的稳定性;
- 缩短图形问题定位时间;
- 让性能比较可重复;
- 让引擎和移植代码共享测试基础设施;
- 将一次性的逆向经验沉淀成可持续的开发工具。
9 - 发布前审计记录
核对日期:2026-10-24
当前范围
- 分支:
static-recomp-handbook - 基线:从 nxnative 干净提交建立的手册分支
- 手册目录:
docs/handbook/ - 发布候选不包含游戏本体、密钥、RomFS、NSP/XCI、存档或用户设备数据
已执行检查
| 检查 | 命令/方法 | 结果 |
|---|---|---|
| Git whitespace | git diff --check | 通过 |
| 相对 Markdown 链接 | 遍历 docs/handbook/*.md 的本地链接目标 | 通过 |
| 症状索引结构 | awk -F '\\t' 检查非注释行列数 | 通过,16 行 |
| 私有标识扫描 | 绝对本机路径前缀、会话标识、内部协作名称、私钥头 | 未发现 |
| 自动化复核 | sh scripts/check_handbook.sh | handbook checks: OK |
已覆盖范围
完成矩阵见 15-completion-matrix.md。当前已经覆盖通用模型、runtime/图形契约、平台差异、 失败案例、AI 诊断、游戏 profile、公开项目解读、许可证边界、实验模板、工具和发布流程。
尚未执行的外部动作
- 没有创建或推送新的 GitHub 仓库;当前没有得到最终远端 URL。
- 没有提交本地改动;原项目规范要求维护者明确要求后再提交。
- Wonder Recomp、Skyline、Ryujinx/yuzu 的部分源码/许可证仍保持“待核对”,没有把它们写成已审计实现。
这三项不是文档内容缺失,而是发布授权、远端选择或外部版本核验事项。继续发布前应重新运行 14-publishing-checklist.md。
10 - 公开发布前清单
这份清单用于把手册推到一个不绑定 Android、macOS、设备或具体游戏的公开仓库。没有明确远端前,只能完成本地 审计和准备,不应猜测目标仓库。
内容审计
- 每个外部事实都有公开 URL、仓库名、许可证状态和访问日期。
- 能影响许可证判断的代码已固定到 commit/tag,或明确标为“仅读文档/待核对”。
- 私有仓库、私有分支、私有会话、用户设备日志和未授权补丁没有进入内容、链接、截图或示例命令。
- 没有游戏本体、密钥、RomFS/NSP/XCI、shader binary、存档或用户数据。
- 游戏特化参数都带 build ID、适用条件、证据等级和不可推广边界。
- Android、macOS 和具体设备只作为案例维度,不出现在仓库名和通用章节标题中。
- 若发布为独立手册仓库,确认是否保留原项目代码历史;若只公开知识库,应从干净文档树发布,避免把游戏移植源码误当成手册内容。
结构和可读性
-
README.md能从症状索引进入对应章节。 -
symptom-index.tsv每行列数一致,字段可被脚本读取。 - 所有相对链接在干净 checkout 中有效;不依赖 sibling worktree。
- 中文说明保留必要的英文 API/文件名,便于搜索源码和 GitHub 文档。
- 新条目说明“可复用设计”和“不可直接复制代码”的区别。
本地验证
再从一个临时干净 checkout 运行相对链接检查;不要只在当前含有 sibling worktree 的目录中验证。
提交和推送顺序
- 由维护者确认最终仓库名和远端 URL。
- 在本分支查看
git diff --stat与敏感文件扫描结果。 - 维护者明确同意后再
git add、提交并设置远端;提交信息说明这是知识库,不是游戏移植产物。 - 推送后检查 GitHub 页面中的相对链接、许可证说明和仓库可见性。
- 发布首个 tag 后,把 tag 写回来源登记表,后续引用优先使用稳定 tag/commit。
如果选择独立知识库,可先执行:
脚本会先运行手册检查,只复制根许可证、手册 README、手册目录和公开范围说明,并拒绝覆盖非空目录。
当前工作树是从 nxnative 的干净提交建立的手册分支,因此仍包含原项目源码和 MIT LICENSE。这不等于手册已经
成为独立仓库;最终发布前由维护者选择“在现有仓库推送分支”或“导出 docs/handbook 为新仓库”,并重新检查
历史、第三方目录和根 README 的公开范围。
推荐仓库名:static-recomp-handbook、recomp-field-manual、native-recomp-guide。
11 - 贡献、引用和许可证
条目格式
每条新经验使用以下字段:
经验来源分级
自己的实践
可以公开总结。应写清楚项目、平台、版本、实验过程、失败尝试、最终证据和不能推广的边界。失败经验同样有 价值,因为它能防止后来者重复走错方向。
公开外部来源
可以引用公开 README、Markdown、issue、commit 和源码,但必须保留 URL、仓库名、commit/tag、访问日期、许可证和 实际阅读范围。引用外部事实时,不能把自己的推断写成外部项目的原话或已验证结果。
私有来源
私有仓库、私有分支、私有会话、未公开附件、用户设备日志、私有补丁和他人未授权的代码不得进入公开手册,也 不得通过改名、删掉链接或复制片段来规避。若其中包含有价值的通用经验,只能由作者重新抽象成不含私有细节、 不含可识别数据、且不暗示外部项目已公开的独立条目;来源标为“本项目独立实践”,不能伪造公开出处。
代码复用规则
- MIT/BSD/zlib/Apache 等允许复用的代码,也要保留版权和许可证声明。
- GPL/LGPL 或许可证不明的代码,只记录设计、接口语义和独立重实现方案,除非项目许可证兼容且经过明确审核。
- 不复制游戏本体、shader binary、keys、RomFS 或用户数据。
- 外部代码的“同名函数”必须先做 ABI、对象布局、线程模型和资源生命周期对照。
- 引用外部仓库时记录 URL、commit/tag、访问日期和实际使用范围。
- 公开手册不收录私有仓库、私有会话或未授权材料;即使它们对本地排障有帮助,也只能保留在不公开的内部工作记录中。
GitHub 推送前检查
大型游戏资料、密钥、设备日志和临时缓存必须留在仓库外。公开仓库只推手册、工具、测试和可审计的源码。
未来发布
仓库名必须描述知识库而非平台,例如 static-recomp-handbook、recomp-field-manual 或 native-recomp-guide;
不要在仓库名中绑定 Android、macOS、某部手机或某个游戏。
12 - 关键突破复盘:从黑屏到可玩的路径
本章把本项目已经留下的经验抽象成“为什么这一步有效”,而不是只列 commit。数字和地址只属于对应游戏/构建,不能当作通用常量。
Android 侧
| 阶段 | 现象 | 真正推进点 | 可迁移经验 |
|---|---|---|---|
| 早期 | 进程启动后黑屏/崩溃 | 先补 loader、HLE、固定 guest 地址和最小 Vulkan 出帧 | 先证明“能加载、能 present、能画三角形”,再接游戏 |
| 首次出色块 | 画面仍不完整 | 用固定色/最小 draw 把 present、render pass、目标图像链路切开 | 纯色是定位 draw/present 边界的证据,不是最终修复 |
| 出现标题/部分 UI | 部分资源缺失 | 补齐 UBO/常量上传去重的内容语义,而不是只按源地址缓存 | 动态 scratch 地址可能复用;去重 key 必须覆盖内容或版本 |
| 白条/文字缺失 | 背景和部分页面正常 | 通过 Mac 参照、逐 draw、shader/program/纹理链路缩小到特定资源路径 | “能显示一页”只能证明那一页的契约,不能证明全局图形正确 |
| 平台差异显现 | Mac 正常、Android 异常 | 对照 Vulkan 能力、render pass、image 生命周期、shader IO、同步与驱动 | 先找首个跨平台分叉,避免把平台差异归因到游戏资源 |
| 继续推进 | 低层问题与游戏特例交织 | 将通用 runtime 修复、profile 参数和实验结论分开归档 | 下一款游戏只复制通用层和流程,不复制临时地址/开关 |
Mac 侧
Mac 的推进通常更快,不是因为静态重编译天然简单,而是 Metal 路径、开发工具和已有宿主边界较稳定;同时已有的 shader/资源分析链更早暴露了契约错误。历史案例包括:模块加载与 unwind、栈布局、CPU 写纹理、R8/捕获、顶点 ABI、stencil、dispatch 常量、坐标原点、分段和预热。
从这些案例提炼出的顺序是:
- 先让重写后的模块稳定启动;
- 用离线 shader 扫描和配对验证减少运行时猜测;
- 让 Metal 的严格 pipeline 校验尽早暴露 VS/FS 不匹配;
- 用 target snapshot 与 shader 输入隔离“资源没写”与“采样错”;
- 最后处理性能、预热和帧率策略。
为什么 Android 更容易慢
Android 同时增加了 JNI/Surface/ANativeWindow、Bionic/linker、Vulkan capability/layout/sync、驱动差异、Scoped Storage 和更敏感的线程时序。一个 Mac 上被宿主默认值掩盖的契约,在 Android 上可能表现为:静默 dummy、空白 image、非法 feature chain、render pass 不兼容、设备差异或竞态。
因此“Mac 已经能跑”应被当作参照实现和事实来源,而不是 Android 的完成证明;“Android 出了一点颜色”也只能说明一条路径成立。
复盘格式
每个新突破都补四句话:
- 之前的最小可复现症状是什么?
- 哪个不变量/契约被证明不成立?
- 修复为什么只影响该层,而不是把游戏特例塞进 runtime?
- 下一款游戏如何用一个最小检查复用这个经验?
13 - 核心模型与原则
静态重编译到底在做什么
它不是把整个 Switch 变成一台虚拟机,也不是把游戏改写成跨平台源码。典型路径是:
因此性能上限很高:游戏代码不经过 CPU 指令解释;但兼容性责任也更集中。模拟器可以用统一的虚拟硬件 把差异藏起来,静态重编译必须逐项重建游戏已经依赖的语义。
工程上还要把系统拆成四层:游戏特化 profile/生成器、通用 runtime、图形后端、平台外壳。用户提供的游戏资料经过 版本校验后生成独立 game library;APK 或桌面程序本身只负责加载 runtime、资源和工具。这个边界让地址表、shader 事实和游戏 patch 不会污染下一款游戏,也让 Android 的解包、SAF、后台构建与 Mac 的窗口生命周期各自可测试。
三个必须分开的契约
游戏契约
包括 title ID、NSO build ID、patch site、模块布局、shader 表、资源格式、内存请求、存档路径和输入状态。 它们必须进入 profile 或案例,不得散落在通用 runtime 中。
平台契约
包括 AArch64 ABI、TLS/x18、线程调度、页粒度、mmap 权限、Horizon SVC/IPC、文件系统、窗口、GPU API 和驱动能力。 同一个游戏在 Mac 和 Android 上的主要差别通常在这里。
图形契约
包括 NVN 对象生命周期、command buffer 录制语义、shader stage IO、descriptor/binding、纹理 layout/swizzle、 render target、同步和 present。画面“能出一帧”只证明最小链路,不证明这些契约整体正确。
最重要的判断规则
- 先判定在哪一层失败,再改代码。 黑屏可能是 guest 停止、draw 没发生、target 没写、合成丢失或 present 错误。
- 一个可见变化不等于根因。 强制红色、固定窗口 texture、关闭深度测试都可能只是诊断证据。
- 优先修通用语义。
QueryMemory、对象代际、UBO 内容、线程等待和 Vulkan layout 比某个 shader hash 的特判更值得修。 - 游戏特例必须有来源。 每个固定地址、容量、shader hash 或 patch 都要记录 build ID 和反汇编依据。
- 实验需要可回滚。 临时开关、设备文件和 trace 结束后清理;失败实验也要归档。
- 性能优化晚于正确性。 先获得正确画面和稳定状态,再测帧时间、GPU 利用率、内存带宽和 pipeline miss。
- 构建产物必须可追溯。 game library 的 build ID、generator/compiler 版本、flags、patch hash 和 profile hash 必须能从日志或 manifest 还原;可恢复构建优先于“重新跑一遍”。
什么算真正突破
真正突破必须同时满足:
- 现象边界发生稳定、可重复的变化;
- 有证据说明哪个契约不满足;
- 修复不依赖未记录的时序巧合;
- 修复后的行为能进入回归测试或手册;
- 明确哪些部分仍未解决。
“这次没有崩”但没有截图、日志或对照,不算完成;“画面变亮”但不知道为什么,也只能算诊断结果。
14 - 可复用工具与证据产物
本章只登记仓库中可复用的工具和诊断入口;工具的具体参数以脚本的 --help 和对应项目文档为准。
静态分析与重编译
| 工具 | 路径 | 用途 | 产物/注意事项 |
|---|---|---|---|
patch_nso.py | dm/tools/patch_nso.py | NSO 重写、规则集和按 build ID 的 cuts | 原始字节校验、记录模块 build ID;游戏特例必须进入 profile/cut 表 |
nso2elf.py | dm/tools/nso2elf.py | NSO 转 ELF 供 Ghidra/objdump 使用 | 保留输入哈希和 image base |
nso_fnmap.py | dm/tools/nso_fnmap.py | 在版本更新后映射函数/全局地址 | 每个映射必须回到反汇编复核 |
shader_split.py | dm/gfx/tools/shader_split.py | 扫描 shader 容器、UnityFS、SARC 和内嵌 GLSLC | 同时保存 map/facts/pairs;不要把 hash 当作跨游戏语义 |
dmsh_build.py | dm/gfx/tools/dmsh_build.py | 构建/翻译 shader 包 | 保存失败 shader 的 VS/FS/program 组合 |
dmsh_chain.py | dm/gfx/tools/dmsh_chain.py | 串联 shader 处理步骤 | 让离线产物可重建,不依赖设备现场 |
c1_guard.py | dm/gfx/tools/c1_guard.py | 检查 shader/编译器输出的回归约束 | 适合放进 CI 或构建前检查 |
运行时取证
仓库已有的 DM_FS、DM_SVC、DM_THR、DM_WATCH、DM_WATCH_STALL 等开关可用于定位服务、线程和停滞。图形侧的 NX_TRACE_* 开关、pass/command/UUB/sampler/glyph 追踪应按需短时开启;高频日志可能改变竞态,必须记录“诊断开关本身可能扰动时序”。
推荐产物目录:
设备验证
设备实验必须显式记录序列号、API 版本、GPU、驱动、页大小、安装包哈希和数据目录。多台 ADB 设备时,所有命令都使用明确的 adb -s <serial>;不要用当前默认设备替代目标设备。游戏本体、密钥、存档和用户数据不进入手册分支。
工具的边界
工具能缩短取证,不会自动证明契约。每个工具条目都应附:输入格式、输出字段、已知误报、适用游戏/SDK、最小回归用例。
手册本身的发布前检查可运行:
它只检查文档相对链接、symptom-index.tsv 列数、私有标识扫描和 Git whitespace,不读取游戏数据,也不要求
设备在线。
若需要导出不带原项目源码的独立手册目录,可使用 scripts/export_handbook.sh;它不是自动推送工具,远端和
仓库可见性仍需维护者确认。
15 - 来源登记表
本表避免把“我看过一个 README”误写成“代码已经审计”。状态分为:已读文档、待读代码、只作语义参考、
可复用但需保留许可证、禁止直接复制。
本轮公开资料核对日期:2026-10-24。外部事实以表中仓库链接为准;未固定到 commit/tag 的条目不得被表述为 “源码已逐行审计”,后续应补稳定版本和具体文件。
来源只分两类:公开来源可以带链接和许可证登记;私有来源不进入本表,不进入公开分支。私有材料产生的通用 经验若要公开,必须重新用公开证据或本项目独立实验表达,不能暴露私有仓库、会话、文件名或内部标识。
| 来源 | 类型 | 当前登记 | 可贡献给手册的内容 |
|---|---|---|---|
| 本项目独立实践 | 项目自身 | 已读内部实验并抽象公开 | Mac/Android bring-up、失败目录、profile、NVN/Metal/Vulkan 案例;不公开内部路径和原始设备资料 |
| d2r-mobile | MIT(以仓库当前声明为准) | README/Android 文档;HEAD 195ae11dc70f(2026-10-24) | Android shell、离线 shader pack、性能回归、共享 host 分层 |
| SuperMarioWonderRecomp | 需确认当前 LICENSE | 已读 README/Android-port/DEBUG 摘要 | NVN→Vulkan 组织、upload ring、调试条目、Android 风险表 |
| Eden | 需逐版本确认 | DesignOverview;HEAD 908b1e9a3701(2026-10-24) | HLE 分层、Android frontend、shader recompiler、Vulkan backend 组织 |
| Strato | GPL-3.0 | README;HEAD ae1566a48285f(2026-10-24) | Android emulator 的架构线索;禁止未经审核直接复制 |
| Atmosphère/libmesosphere | GPLv2,含特定例外 | README/许可证说明;HEAD 6b0c077b4c60f(2026-10-24) | Horizon kernel 语义和测试向量;默认只作参考 |
| LASRecomp | 需按仓库 LICENSE | README/Linux 文档;HEAD 59b61fd3c65d(2026-10-24) | 跨平台矩阵、Vulkan/Linux ARM64 验收、启动时准备资源 |
| LASRecomp-Android-Plus | 需按仓库 LICENSE | README、docs/android-ondevice-build.md、Android 源码结构;HEAD cca791a3e721(2026-10-24) | Android shell、SAF/解包、手机端生成/编译、可恢复构建、触控和移动 Vulkan;不复制游戏 profile |
| XC3 graphics contract article | 本项目公开文章 | 文章全文;访问 2026-10-24 | 契约重建模型、参数/契约/实现分类、等价类与 oracle、L0–L5 成熟度、检查点重放;XC3 具体数字和未证因果不作通用事实 |
| Ryujinx / yuzu | 需逐仓库/文件确认 | 本地已有 vendored/reference 线索 | shader translation、Horizon service 和内存语义交叉验证 |
| Skyline | 需确认原仓库状态和许可证 | 仅由 Strato README 间接定位 | NCE、Android memory/TLS/异常边界的研究入口 |
引用规范
每次新增外部条目,补充:
外部仓库如果只读到了 README,就只能写 README 证实的事实;不能把推测写成“代码已经解决”。
16 - 来源与公开范围
本手册写了什么
本手册是静态重编译实践的独立知识层,整理三种材料:
- 本项目在重写器、HLE、内存、NVN、Metal/Vulkan、Android 和 Mac bring-up 中得到的可迁移经验;
- GitHub 公开仓库的 README、Markdown、许可证和源码结构所能直接支持的事实;
- 基于上述事实重新推导的排障方法、实验模板和不可推广边界。
“本项目经验”不表示所有原始实验材料都会公开。公开版只保留方法、证据类型、结论等级和必要的抽象案例。
目标架构与当前状态分开记录。20-target-architecture.md 是根据本项目经验和公开项目设计抽象出的未来目标,不是
对当前 nxnative 实现完成度的声明;完成度仍以各项目的代码、测试和 profile 证据为准。
明确没有复制的内容
- 没有复制外部项目的游戏专用地址、shader binary、资源、重放表或设备日志;
- 没有复制私有仓库、私有会话、未公开附件、用户设备数据或未授权补丁;
- 没有把外部 GPL/许可证不明实现直接搬进本项目的 MIT 代码;
- 没有把任何游戏本体、密钥、NSP/XCI、RomFS、存档或用户数据放进手册。
外部代码即使许可证允许复用,也必须在目标项目中保留相应版权和许可证,并单独做 ABI、对象布局、线程和生命周期 审查。本手册默认提供“设计参考”和“独立重实现方向”,不是代码复制许可。
来源状态
外部来源的 URL、许可证、访问日期、HEAD/tag 和阅读范围登记在 10-source-register.md。 如果一个仓库只读到 README,就只记录 README 支持的事实;“可能解决过同类问题”必须标为推断或候选,不写成已验证 事实。无法固定公开版本的来源继续保持待核对状态。
基线
当前工作树从 nxnative 的干净提交建立手册分支;手册新增内容集中在 docs/handbook/,根目录已有源码和项目
许可证仍遵循原项目的发布范围。若将来导出为独立知识库,必须按 14-publishing-checklist.md
重新检查历史、第三方目录、根 README 和许可证,而不能只复制一个目录就宣称完成发布审计。
17 - 目标架构:可复用的静态重编译平台
状态:目标设计,未宣称当前已经实现。
本章记录未来希望形成的静态重编译架构。它吸收 LASRecomp 的优点,也保留 nxnative 已经完成的 Android、guest、 NVN 和 Vulkan 探索成果。目标不是复制某个游戏项目,而是让第二个、第三个游戏的接入成本显著低于第一个游戏。
目标
目标架构必须同时满足四件事:
- 游戏代码能够直接执行在宿主 ARM64 上;
- Horizon/NVN 语义集中在可测试的 runtime 边界;
- Mac、Android 和其他平台共享尽可能多的核心代码;
- 游戏特化参数不会污染通用 runtime,也不会被遗忘在聊天记录里。
LASRecomp 的优势
1. 把一次性逆向工作固化成生成器
它使用生成器处理 NSO、函数边界、ARM64 指令、跳转表、重定位和代码输出。AI 或人工完成的逆向判断被固化成 工具后,后续构建不需要每次重新理解整个二进制。
2. 运行时边界清楚
生成的 game library 通过稳定函数表接入 runtime。游戏代码、Horizon 服务、图形后端和平台外壳有清楚的边界, 因此问题可以先归类为“生成器、runtime、图形或平台”,而不是在一个巨大进程里盲猜。
3. 游戏特化内容显式化
地址、build ID、patch、shader 和资源规则集中在生成器输入、patch 表或 profile 中。它们不会悄悄变成通用 runtime 的永久特判。
4. 构建产物可缓存、可恢复、可追溯
生成器、编译器、flags、patch hash 和 build ID 共同决定 game library 的身份。中断后可以继续,工具链变化后可以 准确失效,不需要“感觉这次应该重新生成”。
5. 平台优化建立在正确性之后
移动端的帧生成、pipeline cache、动态分辨率、上传和驱动调优都有独立开关和测量,不会把性能实验和基本语义 混成一个不可回退的修复。
6. 外部借鉴有边界
可以借鉴设计、测试方法和接口语义;需要复制代码时,先核对许可证、ABI、对象布局、线程模型和生命周期。这使得 公开手册能持续吸收 GitHub 经验,又不会把私有代码或 GPL 实现不加审核地带入项目。
目标分层
层之间的规则
| 层 | 可以依赖 | 不应该依赖 |
|---|---|---|
| Profile/adapter | 所有下层公共接口 | 另一个游戏的地址或隐式全局状态 |
| Recompiler | NSO/ARM64 格式和明确 profile | Android、Metal 或某个 shader hash |
| Horizon/NVN runtime | 稳定 game-library ABI | 某个游戏的临时容量和截图特判 |
| Graphics frontend | NVN 语义和结构化资源描述 | 某一平台的 Vulkan/Metal handle |
| Platform/backend | 公共 runtime/frontend 接口 | 直接修改游戏生成代码 |
Profile 是跨游戏复用的关键
每个游戏新增一个 profile,而不是复制整个 runtime:
manifest 至少包含:
- title ID、base/update 版本和 NSO build ID;
- main/sdk/subsdk 模块及其加载范围;
- 生成器、compiler、runtime ABI 和 profile 版本;
- guest 地址偏移策略;
- 必需的 HLE 服务和特殊资源;
- shader、字体、movie、存档和输入事实;
- 当前已证实、排除、未定和撤回的结论。
从当前 nxnative 迁移到目标架构
不要求一次重写。按以下顺序收口:
阶段 A:标记边界
- 找出通用 runtime 中所有 AAi/AA123/Cuphead 特判;
- 为每个特判写出 title、build、来源和不可推广边界;
- 将临时环境变量分为诊断开关、兼容开关和性能开关;
- 给每个 game library 记录 ABI、profile hash 和 patch hash。
阶段 B:建立扫描和报告
自动生成:
- C/C++ 声明与实现差异;
- HLE/SVC/IPC 调用清单;
- guest memory 和固定地址请求;
- shader program、VS/FS IO、binding 和 texture 使用;
- 未支持指令、未解析函数和 dummy/fallback 计数。
阶段 C:把实验变成契约测试
已经验证的 QueryMemory、物理内存、对象代际、纹理上传、shader IO、command list 和 present 经验,分别变成
最小测试。测试不应依赖完整游戏才能运行。
阶段 D:抽出公共图形前端
NVN 对象、资源生命周期、shader reflection、upload 和 render-pass 语义先独立于 Vulkan/Metal。平台 backend 只 实现能力差异和 handle 转换。
阶段 E:恢复性能优化
正确性基线稳定后,再启用异步 replay、image reuse、upload dedup、pipeline 并行、frame generation 和动态分辨率。 每项优化保留关闭路径和固定场景回归。
哪些东西最值得优先建设
按投入产出比排序:
- profile schema 和构建 manifest;
- C/C++ ABI/HLE 缺口扫描器;
- game library 稳定加载接口;
- guest memory/resource ledger;
- shader VS/FS IO 与 descriptor 校验;
- Mac/Android 结构化 draw trace;
- 可恢复的生成/编译缓存;
- 固定输入路线和截图回归;
- 最后才是异步图形和设备特化优化。
复用边界
高复用
- ARM64 lifter 和代码生成器;
- NSO/ExeFS 解析与重定位;
- game library ABI;
- HLE 框架和公共服务;
- NVN 图形前端和 shader translator;
- Android/Mac 平台骨架;
- 构建缓存、日志和测试工具。
中等复用
- 内存容量和地址空间策略;
- movie、字体、音频和输入服务;
- shader 优化和上传策略;
- 设备驱动 workaround。
低复用
- 固定地址;
- 游戏 patch;
- shader hash 到用途的映射;
- 资源路径和版本号;
- 存档结构和游戏逻辑特例。
成功标准
目标架构不是“第二个游戏也能启动”这么简单,而是:
- 第二个游戏不需要复制第一套 runtime;
- 新缺口能由扫描报告定位到明确层;
- 游戏特化修复能在 profile 中独立回滚;
- Mac 与 Android 使用同一套公共图形事实格式做差分;
- 生成和编译可以缓存、恢复并追溯;
- 性能优化不会破坏正确性基线;
- 手册中的经验能对应到代码、测试或可执行工具。
本章描述的是未来目标,不代表当前 nxnative 已经完成这些分层。当前实现和实验状态仍以各游戏 worktree、
06-case-studies.md、13-milestone-map.md 和对应 profile 为准。
如果未来希望服务原生游戏开发和跨平台 QA,还应结合 23-value-to-game-development.md, 把 Contract IR、resource ledger、shader build graph 和 deterministic replay 做成独立于静态重编译器的公共工具。
18 - 目标完成矩阵
本表用于维护者交接和发布前审计。已覆盖表示对应内容已经有章节和规则;不表示每个外部仓库的源码审计已经
完成。外部来源仍以 10-source-register.md 的状态为准。
| 目标要求 | 主要文档 | 当前证据 | 状态 |
|---|---|---|---|
| 与平台/设备无关的通用模型 | 00-principles.md、02-runtime-contracts.md | game contract / platform contract / graphics contract 分层;不可推广边界 | 已覆盖 |
| nxnative 自身实践 | 06-case-studies.md、13-milestone-map.md | Android/Mac 黑屏、出帧、资源、shader、内存和 bring-up 复盘 | 已覆盖 |
| 失败案例与撤回机制 | 01-workflow.md、05-ai-diagnostic-runbook.md | 证据等级、单变量实验、排除/撤回规则 | 已覆盖 |
| runtime 契约 | 02-runtime-contracts.md | loader、ABI、HLE、VAMM、线程、TLS、FS、时间不变量 | 已覆盖 |
| 图形契约 | 03-graphics-contracts.md | draw→shader→resource→pass→present 排查链 | 已覆盖 |
| Android 与 Mac 差异 | 04-platform-differences.md | API、驱动、Surface、Metal、Vulkan、存储和时序差异 | 已覆盖 |
| AI 快速定位 | 05-ai-diagnostic-runbook.md、symptom-index.tsv | 症状索引、决策树、固定输出格式 | 已覆盖 |
| 游戏特化参数 | 08-portability-profile.md | profile 字段、build ID、patch、shader、资源和输入边界 | 已覆盖 |
| 公开 GitHub 项目解读 | 07-external-projects.md、10-source-register.md | URL、许可证、HEAD/访问日期、实际阅读范围和限制 | 已覆盖;部分来源待核对 |
| 可复用代码/设计边界 | 07-external-projects.md、09-contribution-and-licensing.md | 设计可借鉴、代码须按许可证审核、GPL/未知许可证隔离 | 已覆盖 |
| 私有资料排除 | 09-contribution-and-licensing.md、14-publishing-checklist.md | 禁止私有会话、设备日志、未授权补丁进入公开分支 | 已覆盖 |
| 可长期维护 | 11-experiment-templates.md、12-reusable-tools.md | 实验模板、工具目录、证据产物和回归表 | 已覆盖 |
| 静态重编译分层与构建产物 | 00-principles.md、19-lasrecomp-android-lessons.md | profile/生成器、game library、runtime、平台层和可恢复构建边界 | 已覆盖 |
| Android 产品化经验 | 04-platform-differences.md、19-lasrecomp-android-lessons.md | 解包、版本校验、on-device build、缓存、触控、驱动与移动 GPU 测量 | 已覆盖 |
| 未来目标架构 | 20-target-architecture.md | 通用编译器、runtime、图形前端、平台层和游戏 profile 的目标分层与迁移阶段 | 已记录;未宣称实现 |
| 图形契约研究方向 | 03-graphics-contracts.md、21-contract-reconstruction-research.md | Contract IR、事实 ledger、oracle 分层、等价类、检查点重放和 L0–L5 成熟度 | 已记录;工具尚待实现 |
| 游戏选型与成熟度 | 22-game-selection-and-maturity.md | 按契约能力选择样本,定义从合成测试到 XC3 压力测试的阶梯 | 已记录;样本验证持续进行 |
| 对游戏开发的长期价值 | 23-value-to-game-development.md | Contract IR、资源账本、shader 构建图、GPU trace、确定性回放和跨平台性能 QA | 已记录;未来扩展方向 |
| GitHub 发布准备 | 14-publishing-checklist.md | 敏感扫描、链接检查、提交/推送顺序和候选仓库名 | 已覆盖;待远端确认 |
尚未宣称完成的事项
- Wonder Recomp 的当前稳定源码和许可证尚未固定到可复核 commit;手册只保留公开文档层面的参考。
- Ryujinx/yuzu、Skyline 等项目仍需按具体文件和许可证做更细的交叉核对;当前只作为语义参考登记。
- 最终 GitHub 仓库名、远端和首个公开 tag 尚未由维护者确认,因此没有执行提交或推送。
19 - 平台差异:为什么 Mac 快、Android 难
Apple/Metal
Mac/iOS 的优势不是“没有问题”,而是既有 host、Metal 对象模型和 shader 链已经承担了大量语义。常见问题会 更早表现为具体的 texture、attachment、shader input 或 thread stack 错误。
nxnative 的 Mac 经验包括:CPU-written linear texture、big-stack NVN entry、lower-left origin、stencil copy、 compute dispatch constants、same-pass feedback、render-pass segmentation 和 pipeline prewarm。
Android/Vulkan
Android 额外引入:
- Bionic/linker、JNI/NDK 和 AArch64 线程边界;
- scoped storage、SAF、Surface/ANativeWindow 生命周期;
- Vulkan feature/extension、render pass、layout、semaphore 和 driver 差异;
- SPIR-V descriptor/binding 与 shader cache 分发;
- 不同 Adreno 驱动的格式、同步和性能行为。
所以安卓常见的现象是“层层解锁”:先修 loader/HLE 才暴露内存,再修内存才暴露 render target,最后才看到真正 的 shader、合成或游戏逻辑问题。
成熟 Android 静态重编译项目还会多出一条产品链:用户资料导入、解包、版本校验、手机端生成/编译、后台保活、断点 续编译、game library 缓存和触控输入。这些不属于 Vulkan 后端,应该单独验收。参考 LASRecomp Android 的 on-device build 文档: 它把生成器、编译器、链接器和应用服务拆开,并为失败恢复保留中间对象。
Android 的字体、驱动和系统资源也不能照搬桌面路径。语言代码正确不代表字体槽存在;应分别记录:游戏语言、字体 来源、字体格式、解析结果和实际 glyph 覆盖范围。系统 CJK 字体路径、CFF/TrueType 兼容性以及用户导入字体都应在 设备矩阵中单独测试。
统一验证方法
Mac 是黄金参照,不是 Android 的实现替代品。对同一游戏状态采集同一 draw 的有效输入,再比较 backend 结果。 设备差异实验必须固定 APK、patch、资源、存档、shader cache 和输入路线,只改变驱动/平台。
性能原则
静态重编译的性能上限来自原生 guest CPU 执行、离线 shader、低开销 host 和正确的资源生命周期。先消除错误和 重复工作,再优化异步、pipeline cache、render scale、上传和分段提交;否则高性能只是更快地显示错误画面。
20 - 契约重建研究方向
来源:《主机游戏图形 API 移植中的契约重建问题——以〈异度神剑 3〉的渲染适配为例》
状态:公开文章的可迁移内容整理;XC3 的具体数量、地址、shader key 和当前阻塞不作为通用事实。
这篇文章为什么仍然有价值
它提供的不是某个游戏的修复答案,而是一个适合长期架构建设的问题定义:
图形移植的核心不是逐条翻译 API 调用,而是把原主机由离线工具链、固定硬件和驱动隐含保证的契约,在宿主环境中重新显式化并验证。
这补足了手册中 runtime contract 和 graphics contract 之间的桥梁。静态重编译能保证 guest CPU 指令语义接近原程序, 但不会自动带走 CPU 命令流、shader 变体键、资源元数据、附件格式、同步和驱动保证。
应保留的通用洞见
1. 契约不只在 shader 里
一个 draw 的完整事实至少来自四处:
因此 shader 反编译正确,不代表 draw 正确。后续图形 trace 应把同一 draw 的以下对象关联起来:program/VS/FS/CS、 vertex/index layout 和 bytes、UBO/SSBO/descriptor、sampler/texture view、attachment/render pass、blend/depth/raster、 resource incarnation/dirty/upload,以及 submit/wait/acquire/present 顺序。
2. 缺口要分三类
| 类型 | 典型问题 | 首选证据 | 修复方向 |
|---|---|---|---|
| 参数值错误 | 槽位数、附件数、stride、容量、格式 key | 配置来源、调用参数、反汇编 | 修正 profile/解析规则 |
| 类型/接口契约缺失 | uint/float 输出、VS/FS location、binding、layout | reflection、pipeline create、数值读回 | 补显式契约和校验 |
| 功能实现缺失 | GS、TF、copy、同步、资源生命周期空壳 | 同 draw 生产/消费链和副作用 | 实现、选择性替代或明确降级 |
参数问题不应重写函数,接口问题不应靠扩大内存,空实现问题也不应仅靠关闭验证来掩盖。
3. 产物数量不是正确性指标
shader key 数量、pipeline 数量、成功生成文件数和编译时间都只是成本或覆盖指标,不能证明附件格式、shader 输出类型、 binding、GS/TF/compute 生产消费关系或最终像素数值正确。进度应拆成:产物可生成、管线可创建、合成 draw 可执行、 固定场景像素通过、长时间回归通过。
4. 用约束和等价类降低组合爆炸
不要逐个穷举所有 shader × attachment × binding 组合。先从 reflection、格式规则、编译器和 pipeline validator 得到 约束,再把行为相同或契约相同的候选聚类,每个等价类只选代表做宿主确认。
编译器/验证器可以排除非法组合,但不能单独证明数值语义正确;视觉模型可以筛查灾难性帧,不能取代 pixel readback、 数值比较或资源快照。
5. 契约层精确,实现层可以替换
契约必须准确:输出类型、附件格式、binding、同步和可观察副作用不能随意改变。但内部实现未必必须复刻原硬件, 例如某些 geometry stage 可用 compute 替代,某些反馈缓冲可用显式 ping-pong 资源替代,某些绑定布局可映射到宿主 descriptor 体系。是否替代必须由同 draw 的生产/消费证据决定。
值得继续深挖的架构课题
A. Graphics Contract IR
建立一个与 Vulkan/Metal 无关的中间表示,描述 program 和 draw 的完整契约:
Metal 和 Vulkan backend 都从这份 IR 生成各自对象。这样 VS/FS 不匹配会在公共层被发现,而不是等某个平台静默画错。
B. Contract ledger 与事实来源
每条事实记录 fact_id/title_id/build_id/program/draw/value/source/confidence/last_verified/regression。来源可以是反汇编、
CPU trace、shader reflection、资源 dump、Mac 对照或宿主读回。推断值和已证实值分开,事实改变时保留历史。
C. Oracle 分层
把验证器拆成四级:结构 oracle(格式、类型、binding、IO)、数值 oracle(UBO、texture、buffer、pixel)、时序 oracle (submit、同步、资源可见性、帧生命周期)、视觉 oracle(黑屏、白屏、文字、布局和特效筛查)。高层 oracle 不能替代 低层 oracle。
D. 检查点加确定性重放
对于启动慢、设备反馈稀缺或竞态敏感的游戏,优先实现“帧边界检查点 + 固定输入重放”,而不是立即追求任意时刻的 完整 save state。检查点至少绑定 guest memory、逻辑线程边界、资源事实、shader/pipeline cache 和输入序列。
E. 能力成熟度 L0–L5
| 等级 | 含义 |
|---|---|
| L0 | 尚未识别或仅有猜测 |
| L1 | 静态清单和调用范围完成 |
| L2 | 离线候选和结构约束完成 |
| L3 | 合成测试/管线验证通过 |
| L4 | 真实游戏 draw 或场景通过 |
| L5 | 数值/像素回归和长时间运行通过 |
这比“实现了多少 API”更能显示真实进展,也能防止把标题画面出帧误认为图形系统完成。
F. 游戏复杂度分类器
新游戏接入前,可以先按 CPU 特化程度、shader 变体规模、geometry/transform feedback/compute 使用、非标准 render target、资源生命周期、引擎跨平台抽象和可获得参照系估算风险。这个分类只用于排序,不断言某类游戏一定容易。
对 nxnative 的直接转化
文章最值得转成代码的不是某个 XC3 特判,而是:
- 在公共图形层引入
ProgramContract/DrawContract数据结构; - 把 shader、纹理、attachment、同步和 present 事实写入统一 ledger;
- 将结构、数值、时序、视觉验证器分层;
- 将 AAi 当前实验结果映射到 L0–L5 和证据等级。
这四项完成后,AA123、AAI、Cuphead 或其他游戏可以共享诊断工具,即使它们的 profile 和 shader 仍然不同。
不应直接搬入通用层的内容
- XC3 的具体 shader 数量、地址、槽位和附件格式;
- 尚未取得同 draw 生产/消费证据的 TF/GS 因果结论;
- 只在某个平台观察到的驱动行为;
- 文章中对其他项目的概括,若没有对应公开源码或文档证据。
这些内容应留在 XC3 profile、研究文章或候选事实登记中,不能变成 nxnative 的默认契约。
21 - 实验与证据模板
本章是可复制的工作表。每次实验先复制模板,再执行;不要只把结论写进聊天记录。
1. 单变量实验
“开关改变后画面变了”只证明开关影响了执行路径,不自动证明它是根因。必须记录基线和回滚结果。
2. Mac/Android 或两台设备差分
先找“首个分叉”,不要拿最终黑屏直接对照。Mac 能跑通通常是契约参照系,不代表其实现可以原样搬到 Android。
3. 图形 draw 取证
至少同时保存以下字段:frame、command list、draw 序号、VS/FS hash、program/pipeline、render target、viewport/scissor、channel mask、blend/depth/stencil、顶点缓冲地址/长度、纹理对象与 view、descriptor、shader IO、present 时间点。
判读顺序:
- draw 是否录制并提交;
- 是否进入目标 render pass;
- 顶点/索引是否非空且地址有效;
- program 是否成功、VS 输出与 FS 输入是否匹配;
- descriptor 是否真实对象而非 dummy;
- image 内容、swizzle、layout、barrier 是否正确;
- blend/深度/模板/通道掩码是否允许写入;
- composite 是否采样了正确 target;
- present 是否提交了同一张图。
4. 内存账本
每次检查这些不变量:映射区不重叠;unmap 只释放自己拥有的区间;used 不超过 max;SetHeapSize 与 MapPhysicalMemory 使用同一资源账本;对象销毁后不能被旧 command list 或 image reuse 引用。
5. 回归记录
22 - 外部项目解读
本章只记录公开仓库中已经核对过的设计。代码是否能直接复制,必须另看许可证和依赖;“同样的文件名”不代表 “同样的契约”。
d2r-mobile
它的 README 明确把 host、Android shell、touch overlay、Vulkan presentation 和离线 shader pack 分开,并报告 Android ARM64 真机结果。值得借鉴的不是把它的游戏参数搬过来,而是:
- Android 只分发不含游戏本体的 shell,运行时导入资源;
- shader 在电脑上预翻译并随 import pack 分发,减少首场景 miss;
- Android 与 Apple 共享 host,平台差异集中在前端和图形后端;
- 有 host/gfx 单测、重写等价检查、脚本化输入回归和设备 smoke test;
- Vulkan presentation、touch 和性能工作有独立文档,而不是混在游戏 profile 中。
对应 README 的 Android 章节和 docs/ANDROID.md、docs/ANDROID_COLLABORATION.md。可复用的是组织方式和测试门槛,
不能直接复制其游戏专用 replay、地址和资源规则。
Eden / Yuzu
Eden 的设计文档明确说明:服务经常以 HLE 方式实现,目标是对程序提供足够准确的语义,而不是模拟每个物理设备; 其 Android 前端、shader recompiler 和 Vulkan backend 分层也值得参考。对 nxnative 最有用的是:
- 把服务语义、CPU、视频和前端隔离;
- 将 memory/page-table/backing/resource-limit 作为可测试的账本;
- 把 shader translation 和 graphics backend 分开;
- 用 HLE 返回“合理的空状态”,而不是任意成功/零值。
Eden/Yuzu 代码许可证和依赖必须逐文件核对;GPL 代码不能直接移植进 MIT/其他许可证项目。
LASRecomp
它展示了同一游戏的 Windows/Linux Vulkan 与 macOS Metal 分层,以及 Linux ARM64 的明确“编译但未真机验证”状态。
可借鉴:平台能力矩阵、启动时准备本地游戏文件、Vulkan 性能回归、make smoketest 一类验收门槛。不能把
“Linux ARM64 可编译”误写成“Android 已支持”。
Android fork:Nycolazs/LASRecomp-Android-Plus
这是本手册目前最适合研究“如何把静态重编译交付到 Android”的公开案例。它的公开 README 和 on-device build 文档 显示了完整链路:用户提供游戏文件和 keys,应用解包并校验版本,手机运行生成器和 AArch64 编译器,最后加载单独的 game library。它还把 Android 前端、触控、GPU 驱动选择、Vulkan 性能、shader/pipeline cache 和后台构建拆成不同 边界。
可借鉴:
- 公共 APK 检查,保证不含游戏代码和游戏资料;
game build id + generator/compiler + flags组成可失效的构建缓存 key;- 中断构建可恢复,构建产物原子发布;
- Android 地址偏移由生成器、patch 和 runtime 共同管理;
- 移动 GPU 优化以固定场景和温度/帧时间测量为准;
- 触摸、解包、字体、渲染和输入日志有各自的诊断范围。
不可直接搬用:Link’s Awakening 的地址表、shader/资源事实、游戏 patch、存档结构以及它的 Android 专属假设。该 fork 的代码可以作为公开实现研究,但每个文件仍需按其许可证和依赖逐项核对。更完整的分层和工作量拆解见 19-lasrecomp-android-lessons.md。
SuperMarioWonderRecomp
仓库:BelmanteGu/SuperMarioWonderRecomp
这是与 nxnative 最接近的公开 NVN→Vulkan 静态重编译参考之一。其文档把内容分成 recompiler、shared runtime、
runtime/src/nvn、每游戏 glue、Android build 和 docs/nvn 调查记录。特别值得吸收的是:
docs/nvn/ANDROID-PORT.md先写“哪些可继承、哪些仍是风险”,没有把桌面 MoltenVK 成功误当成 Android 真机验证;- guest 指针、固定地址、heap/stack/thunk 区域在 runtime 文档中集中定义;
- Vulkan 资源通过 per-submit upload ring 进入 GPU,明确记录了它和 external-memory-host 的取舍;
- Android manifest、最低 Vulkan/API 要求和构建输出单独验收;
docs/nvn/DEBUG.md按“症状、根因、修复、开关”积累图形问题,并记录错误假设和新诊断工具。
这直接对应 nxnative 的三个教训:不要把 upload dedup 当成无害优化;不要把目标/窗口 image 混成一个全局对象; 不要在没有 stage/target/descriptor 证据时凭截图猜 shader。该仓库的代码和文档仍须逐文件确认许可证,手册只引用 设计和公开事实,不复制游戏特化实现。
Strato / Skyline
Strato README 明确说明它是 Android ARMv8 Switch emulator,许可证为 GPL-3.0,并说明其基础来自 Skyline、shader compiler 源自 yuzu 的 fork。它适合作为 Android guest 边界、NCE、线程/TLS、内存映射、Vulkan capability 和移动 GPU 兼容性研究入口;不适合作为 nxnative MIT/自有代码的直接复制源。
使用规则:
- 只记录接口语义、设计取舍和可独立重实现的不变量;
- 需要复制代码时先停止,核对 GPL 兼容性和目标仓库的许可证;
- 不把 emulator 的动态 CPU/系统模型直接套到静态重编译的 guest ABI;
- 把 Skyline/Strato 的“Android 已处理过”当作候选契约来源,而不是 AAi 的证明。
Atmosphère / libmesosphere
libmesosphere 是 Horizon kernel functionality 的 C++ 实现,README 标注 GPLv2,并列出对 yuzu、Ryujinx 等项目的
特殊许可例外。它最适合查:address arbiter、thread/process 状态、SVC 返回语义、内核对象生命周期和内存布局。
除非许可证明确允许且项目审核通过,否则 nxnative 只采纳语义和测试向量,不复制实现。
本项目独立实践
本项目的长期实验记录展示了另一种重要经验:必须保留撤回、错误假设和自动评分器,否则同一“坏帧”集合被 误记后,后续二分会建立在假数据上。公开版不引用内部 worktree、会话或设备日志。
仍需继续核对
- Ryujinx / yuzu:SVC、address arbiter、page table、NVN shader reflection 和资源限制语义的具体 commit。
- Skyline 原始仓库:与 Strato 的继承关系、NCE 异常/TLS 代码和许可证边界。
- Wonder Recomp 的当前 LICENSE、具体 Vulkan runtime 文件和最新 Android 真机状态;GitHub 页面有时返回缓存/404, 在未取得稳定源码快照前不记录为“可直接复用”。
23 - 游戏特化 Profile
每个新游戏都应先创建 profile,再修改通用 runtime。建议至少包含:
| 类别 | 字段示例 | 必须记录 |
|---|---|---|
| 身份 | title ID、base/update、NSO build ID、SDK | 来源文件、SHA、版本 |
| 模块 | main/sdk/subsdk、映射、bss、异常信息 | module layout 和 loader 证据 |
| 重写 | patch site、原始 bytes、替换原因 | 反汇编前后、适用 build |
| 内存 | guest block、heap、alias、stack、页粒度 | 游戏请求值 vs 宿主限制值 |
| 图形 | shader map/pair、binding、format、swizzle、origin | shader hash、stage、参照截图 |
| 资源 | RomFS marker、Addressables、movie、font、save | 读取路径、缺失行为、大小/hash |
| 输入/状态 | 按键、touch、焦点、首轮存档 | 可回放序列和存档前置条件 |
| 性能 | 分辨率、present interval、GPU/CPU预算 | p50/p95/p99、温度和设备 |
通用层的准入条件
某项修复只有在以下条件都满足时才进入通用 runtime:
- 不依赖 title ID、固定 guest address 或单个 shader hash;
- 至少一个单元/离线测试覆盖;
- 至少一个真实游戏回归;
- 不破坏已有 profile;
- 能解释原始平台契约,而不是仅让当前截图变好。
否则放进 profile,并写清楚为什么不能推广。
24 - 游戏选型与架构成熟度阶梯
状态:目标方法,具体游戏的可用性和版本仍需按公开仓库、许可证和本地资料单独核对。
核心原则
判断静态重编译架构是否“做厚”,不能只看已经跑通了多少款游戏。应该看覆盖了多少种不同的契约类别:
一个只使用普通 vertex/fragment、单一 render target 的游戏,不能证明 NVN frontend 已经支持 Geometry Shader、 Transform Feedback、整数附件、复杂资源生命周期或多阶段 compute。
推荐的能力阶梯
L0:合成图形契约测试
不依赖完整游戏,先验证:
- guest buffer/texture/image/view;
- vertex/index 和 uniform;
- 普通 vertex/fragment draw;
- blend、depth/stencil、viewport/scissor;
- render target、copy、resolve、acquire/present;
- compute 写入后被 graphics 读取;
- shader VS/FS IO 和 descriptor 校验。
这是所有真实游戏之前的“图形单元测试层”。
L1:简单 2D 或 Unity 游戏
代表特征是 sprite、UI、字体、普通纹理和简单粒子,重点验证:
- 普通纹理采样和 swizzle;
- alpha/blend;
- 动态字体图集;
- 小型 command buffer;
- 输入、窗口和 present;
- 资源上传和销毁。
这一级适合快速发现对象模型和资源生命周期的结构性错误,但不能证明复杂 3D 图形完整。
L2:中等 3D、已有成熟参考实现的游戏
代表特征是完整 3D 场景、电影、存档、多个 render pass 和跨平台运行。重点验证:
- 静态重编译 generator 与 game-library ABI;
- Horizon runtime 基本服务;
- 3D camera、depth、shadow 和后处理;
- movie/audio/font 资源链;
- Mac/Android 或 Metal/Vulkan 差分;
- 构建缓存和长时间运行。
LASRecomp/《织梦岛》适合作为这一层的公开架构参考,而不是把它的游戏特化参数直接搬入其他项目。
L3:复杂 NVN/Vulkan 游戏
代表特征是 shader variant 多、移动 GPU 敏感、资源和 pass 关系复杂。重点验证:
- program key 到 VS/FS/CS 的稳定映射;
- descriptor/binding 和 shader reflection;
- pipeline cache、shader 预热和异步编译;
- image reuse、upload dedup 和资源 incarnation;
- 多帧 in-flight、submit/wait/present;
- Android 驱动能力和性能回归。
Super Mario Wonder 这类公开 NVN→Vulkan/MoltenVK 项目适合作为该层的参考样本;其代码和许可证仍需逐文件核对。
L4:深度硬件特化的最终压力测试
代表特征是大型自研引擎、非标准格式和多个强耦合图形阶段:
- Geometry Shader;
- Transform Feedback;
- 整数或打包 render target;
- G-buffer、光照、阴影、植被、粒子和后处理;
- 多阶段 compute 生产/消费;
- 大型世界 streaming 和复杂内存生命周期。
XC3 属于这一层。它应该作为架构成熟后的压力测试,而不是用来发现最基础的 TextureBuilder、present 或普通 blend 错误。
推荐的最小验证组合
如果资源有限,建议覆盖以下 4–6 个层次,而不是盲目增加游戏数量:
| 顺序 | 样本 | 主要目的 |
|---|---|---|
| 0 | 合成测试 | 验证公共图形契约,不依赖游戏数据 |
| 1 | Cuphead 一类 2D/Unity 项目 | 普通纹理、UI、字体、blend、输入 |
| 2 | 《织梦岛》/LASRecomp | 完整静态重编译和跨平台 runtime 基线 |
| 3 | AA123/AAI | Unity/IL2CPP、动态 UI、movie、资源生命周期 |
| 4 | Super Mario Wonder | 复杂 NVN→Vulkan、shader 和移动 GPU |
| 5 | XC3 | GS/TF、整数 RT、大型自研引擎和强耦合管线 |
这里的游戏是能力样本,不表示应复制它们的地址、shader、资产或私有资料。
什么时候可以说公共图形前端“足够厚”
至少需要同时满足:
- 普通 texture/buffer/render pass/present 在合成测试中稳定;
- 每种关键 shader stage 至少有一个真实游戏和一个合成测试案例;
- 普通、压缩、整数、depth/stencil 和多附件格式都有明确事实和回归;
- compute、GS、TF 或其有证据的替代路径有生产/消费链测试;
- Mac 与 Android 能使用统一的 draw/resource trace 做差分;
- 新游戏的普通启动问题主要新增 profile,而不是复制或重写 NVN frontend;
- 连续两个新游戏接入时,公共 runtime 的改动能被契约测试覆盖;
- 异步、image reuse、upload dedup 和性能优化都有关闭路径,不会破坏正确性基线。
游戏选择的反模式
- 连续选择三个只使用普通 2D draw 的游戏,然后宣称图形前端通用;
- 只看能否进入标题画面,不测试电影、菜单、存档、长时间场景和资源流式加载;
- 把 shader/pipeline 数量当成契约覆盖率;
- 直接从最复杂游戏开始,导致基础 runtime 缺口和特化图形缺口同时出现;
- 为了让单个游戏通过,把地址、容量或 shader hash 写进公共层;
- 只在一个 GPU/驱动上验证异步路径,然后把它当作平台无关语义。
对当前路线的意义
当前 nxnative 不需要暂停所有目标去移植一长串游戏。更有效的方式是:
- 先把 AAi/AA123 当前问题抽成合成契约测试;
- 用已有 Cuphead、LASRecomp 和 Super Mario Wonder 公开资料校准能力矩阵;
- 将 AA123/AAI 作为中等复杂度的 runtime/资源样本;
- 把 XC3 的工作放在 Contract IR、事实 ledger 和 oracle 分层之后;
- 每完成一个能力类别,就把测试和 profile schema 回写公共仓库。
这样每个游戏都贡献一类可复用能力,而不是只贡献一份不可迁移的修补记录。