一张 2026 年的手机能不能原样跑一部 x86 架构的 Windows PC 游戏?9 月末开源的 Madeira 项目给出了一个接近完成的答案:不依赖任何云游戏串流,所有计算都发生在手机本地。它把 Wine、FEX-Emu、DXMT 三个桌面平台的老牌兼容层搬到了 iOS 上,Thumper 和 ULTRAKILL 已经可以完整游玩,《漫威宇宙入侵》进入过可玩阶段,Red Dead Redemption 2 在 iPhone 18 Pro 上跑出了接近 30 fps、接近 720p 的成绩。
这是个人开发者 Will Faust 的研究项目,代码以 GPL-3.0-or-later 许可证发布在 GitHub。过去半年里同类尝试都停留在"演示视频"阶段,而 Madeira 把整条技术管线做了出来,并且公开了完整的架构分析文档。要理解它做了什么,需要先看清 iOS 给这类项目出的三道难题。

三道难题:JIT、fork 和图形 API
x86 代码不能直接在 ARM 芯片上运行,需要即时编译(JIT)在运行时把 x86 指令翻译成 ARM64 指令。没有 JIT,模拟器只能退回到逐条解释执行,速度差 10 到 50 倍。而 iOS 对 JIT 的限制是整套方案的第一道门槛。
苹果从 iOS 18.4 起禁止 App Store 应用使用 JIT,例外只有获得 BrowserEngineKit 授权的第三方浏览器引擎。开发者社区绕过的方式依赖调试协议:StikDebug 通过回环 VPN 连接设备自身的 debugserver,发送 vAttach 附加到目标进程,内核会给进程打上 CS_DEBUGGED 标记,这个标记在调试器断开后依然保留,进程从此获得执行 JIT 代码的资格。
iOS 26 引入的 TXM(Trusted Execution Monitor)把这个口子收紧了。在新系统上,附加后立即断开的操作不再有效,调试器必须在 JIT 使用的整个生命周期内保持连接。应用在需要可执行内存的位置预先嵌入 BRK 断点指令,调试器拦截这些断点,再通过调试协议把对应内存页标记为可执行。每一次代码页的写入执行切换都要走一遍这个流程。这正是 Madeira 必须以侧载方式安装、无法上架 App Store 的原因:JIT 权限来自调试器附加,而 App Store 分发的应用拿不到这种权限。
第二道难题出在 Wine 的进程模型。桌面 Linux 上,Wine 用一个独立守护进程 wineserver 管理全部 Windows 内核状态,包括句柄、互斥锁和注册表,而 iOS 不允许应用调用 fork(),独立守护进程的方案直接失效。Madeira 的处理方式是把 wineserver 做成主进程内的一个线程,整个 Windows 兼容栈跑在同一个 Mach 进程里。README 里专门用一句话强调了这个结构:wineserver 以线程而非独立进程存在。这不是性能优化,是 iOS 沙箱约束下的唯一解。
第三道难题是图形 API。Windows 游戏的渲染调用走 DirectX,iPhone 的 GPU 只认 Metal。三种 DirectX 版本对应三条不同的翻译路径,复杂度差异很大。
三层管线如何协同工作
Madeira 的完整调用链是这样的:x86-64 的 Windows 游戏可执行文件先进 FEX-Emu 做指令翻译,产出的 ARM64 代码调用 Wine 提供的 Windows API 实现,Wine 再把图形调用交给 DXMT 翻译成 Metal,最终由苹果 GPU 渲染。
第一层的 FEX-Emu 负责最重的活。它是一个带 SSA 中间表示和优化通道的 x86-64 到 ARM64 翻译器,同类工具 Box64 也能做指令翻译,但 Box64 的 JIT 内存管理依赖同时可读、可写、可执行的内存页(RWX),这种内存在 iOS 上被 W^X 策略明确禁止,一个页面绝不允许同时处于可写和可执行状态。FEX-Emu 把内存管理抽象成可插拔的接口,可以适配 iOS 的 W^X 约束,这是选择它而非 Box64 的决定性理由。
W^X 的具体实现用了模拟器社区验证过的双映射技巧:同一段物理内存建立两个虚拟映射,映射 A 只读可写,映射 B 只读可执行。编译器线程往 A 写入生成的机器码,执行线程从 B 运行,两个映射指向同一份物理页,任何时刻都没有哪个页面同时可写又可执行。这套做法来自在 iOS 上跑 Switch 模拟器的 MeloNX 项目,配合 VM_LEDGER_FLAG_NO_FOOTPRINT 标志,JIT 缓存的内存还能不计入应用的 Jetsam 内存配额。
第二层的 Wine 以原生 ARM64 运行,这一点对性能至关重要。Wine 提供了一个名为 xtajit 的插件接口(名字来自 Windows 自己的 x86 模拟层),要求模拟器以 DLL 形式导出 BTCpuProcessInit、BTCpuSimulate、BTCpuGetBopCode 三个入口。FEX-Emu 恰好提供 libwow64fex.dll 作为这个接口的实现。分工由此确定:Wine 的数百个 DLL 全部以原生 ARM64 速度执行,只有游戏自身的 x86 代码经过模拟,整个 Wine API 层不承担模拟开销。架构文档估计,相比把整条 Wine 栈一起模拟,这个设计省下的性能是决定性的。
配合 Wine 11.0 的 WoW64 模式,32 位 Windows 游戏也能运行:32 位 API 调用在内部被转发到 64 位实现,宿主系统完全不需要存在 32 位进程。iOS 从头到尾没有支持过 32 位应用,这条路径让 Dead Space、Mirror's Edge、Far Cry 3 这批 32 位老游戏找到了在 iPhone 上运行的办法。
第三层的 DXMT 处理 DirectX 11。它的特别之处是完全绕过 Vulkan,把 DX11 的着色器(DXBC 字节码)直接编译成苹果的 AIR 位码,再生成 Metal 着色器库,与 Metal 原生着色器编译管线走同一条路。几何着色器的处理也被解决:Metal 没有原生的几何着色器阶段,DXMT 把它转换成 Metal 的 mesh/object 着色器。这一层是三个组件里移植工作量最小的,因为它原本就跑在 macOS 上,底层 Metal API 与 iOS 一致,移植主要涉及把 SDK 从 macosx 换成 iphoneos、把 MTLCopyAllDevices() 换成 iOS 专属的 MTLCreateSystemDefaultDevice()。
DirectX 12 游戏走哪条路
RDR2 和 Cyberpunk 2077 这类新游戏用 DirectX 12 渲染,这是整条管线里最难的部分。架构文档给出两条路径。
近期路径是 VKD3D-Proton 把 DX12 翻译成 Vulkan,再由 MoltenVK 把 Vulkan 翻译成 Metal,两层叠起来。这条链的绝大部分需求 MoltenVK 都已满足:Vulkan 1.3、robustness2、push descriptor、自定义边框色、BC 纹理压缩(A15 及更新芯片),逐项可用。唯一真正的拦路虎还是几何着色器:DX12 的 11.0 特性级强制要求几何着色器,MoltenVK 因为 Metal 没有对应阶段而无法原生支持。解决方案同样有先例,Ryujinx 和 MeloNX 把整个顶点加几何着色器管线在 IR 层转换成计算着色器,分三个阶段执行,这套方案已经在 iOS 设备上 shipping。上游的 MoltenVK 也有一个处理几何着色器模拟的 PR 在推进。
长期路径是写一个开源的 DX12 到 Metal 直接翻译层,绕过 Vulkan 中间层,用苹果的 Metal Shader Converter 处理着色器转换。架构文档把它列为最优性能的终态,描述符堆到 Argument Buffer Tier 2 的映射被标注为其中最难的部分。
作为参照,苹果在 macOS 上的官方方案 Game Porting Toolkit 用的是 Rosetta 2 加 D3DMetal 的组合。iOS 上 Rosetta 2 不存在,D3DMetal 不开源,所以 Madeira 的等价替换是 FEX-Emu 加 DXMT,整个栈保持开源。
实际表现:能玩,但边界清晰
截至本文可查证的项目状态,各游戏的运行情况分几档。Thumper 和 ULTRAKILL 可以游玩;Marvel Cosmic Invasion 进入了游戏画面但控制尚不可靠,还出现过一次原因不明的进程终止;其他多数游戏在低帧率下到达游戏画面。
RDR2 的进展最能说明这条管线的潜力。初次运行只有个位数帧率和大量图形错误,经过多轮构建迭代后达到接近 30 fps、接近 720p。内存是一个硬约束,RDR2 的内存占用约 7.6 GB,而 iPhone 整机只有 8 GB 物理内存供所有应用共享。项目为此实现了类似交换分区的机制,把部分内存换出到存储,为把占用压到 8 GB 设备可运行的水平留了空间。
GTA V 也已经跑起来,一个有趣的细节是 Wine 识别出的 SoC 型号是 M5。另外 Steam 客户端本体空载就吃掉近 2 GB 内存,项目的对策是 Madeira Dock,一个无界面的 Steam 客户端,只加载经过验证的必要 Steam DLL。需要说明的是这不是 DRM 绕过,Steam 平台的游戏仍要走正版验证,无 DRM 的 GOG 游戏是另一个被提及的选择。游戏自带的反作弊系统在模拟环境下的行为目前仍是未解问题。
架构文档对性能上限做过推算:在软件 TSO 模式下,A18 Pro 芯片上的 DX11 游戏大约能跑到原生 x86 性能的 38% 到 52%,启用硬件 TSO 后可以到 52% 到 67%。硬件 TSO 是 Apple 芯片的一个特殊模式,x86 架构对内存访问顺序的要求比 ARM 宽松,TSO 模式让 ARM 芯片按 x86 的内存序执行,省掉大量内存屏障指令。Rosetta 2 在 macOS 上就靠它拿到了约 80% 的原生性能,而 iOS 上能否启用这个模式还是个开放问题。
发热是另一个现实约束。持续高负载的 CPU 加 GPU 运算在手机上必然触发温控降频,长时间游戏的帧率会在最初几分钟后回落。
许可证工程与 AI 辅助开发
Madeira 的许可证安排值得单独一段。项目本体是 GPL-3.0-or-later,三个上游组件的处理各不相同:Wine 原本是 LGPL-2.1,作者援引 LGPL 第 3 条(该条款明确允许把副本改按普通 GPL 发布)把 fork 重授权为 GPL-3.0;FEX-Emu 和 DXMT 是 MIT,fork 保留上游 MIT、修改部分按 GPL-3.0 发布。文档里专门写明这套安排的边界:许可证义务在分发时才触发,自己私下修改不发布不受约束;经 Madeira 运行的游戏本身是独立作品,不在许可证覆盖范围内;已经以 MIT 发布的 FEX-Emu 和 DXMT 代码不会因为这次 fork 撤回授权。
另一个细节写在贡献政策里:FEX-Emu 上游的贡献政策禁止在给上游的提交中使用 AI 生成的代码,而这个 fork 包含大量 AI 辅助的工作,文档明确提醒不要把这个 fork 的改动提交回上游。 fork 与上游之间这条清晰的界线,是开源社区里比较少见的处理方式。
还有一块拼图不属于项目代码:游戏依赖的 Microsoft Visual C++ 运行库的 12 个 DLL 文件由微软授权发行,项目不分发,需要用户自己从官方 VC_redist 安装包里提取,并且必须保持原样,不能剥离 Authenticode 签名。
它证明了什么
Madeira 没有使用任何苹果未公开的私有 API 特权,它的每一步都站在社区已有成果上:JIT 权限来自 StikDebug 的调试协议方案,双映射内存来自 MeloNX,图形翻译站在 DXMT 和 MoltenVK 的肩膀上。它的贡献是把这条开源管线第一次完整地跑通在非越狱的 iOS 设备上,并用 GPL-3.0 把成果固定下来。
它同时声明了自己的边界:这是一个研究项目,不是产品,README 里写明预期存在毛边、单游戏兼容性问题和破坏性变更。JIT 机制随时可能被苹果进一步收紧,项目文档把这一点列在风险清单的首位。对关心跨平台兼容层的开发者来说,它现在是一个难得的开源参考实现,Windows API 翻译、x86 动态翻译、图形 API 桥接三个领域的工程细节都能在其中找到对应的 iOS 版答案。
来源: