1. 从“Madeira”这个名字说起:它到底是什么
第一次看到“Madeira”这个词,很多人第一反应是葡萄牙那个盛产葡萄酒的海岛,或者是一杯带着焦糖风味的马德拉酒。但如果你混迹于移动端模拟器、跨平台兼容层或者 iOS 开发圈,这个名字大概率指向的是另一回事——一个围绕Wine、FEX-Emu、DXMT构建的、目标直指在 iOS 设备上运行 x86-64 Windows 程序的实验性项目。
我接触这个方向有一段时间了,最初是从“iOS 上能不能跑 Windows 软件”这个朴素问题开始的。市面上能查到的资料要么是零散的论坛帖子,要么是语焉不详的仓库 README,真正把 Wine、FEX-Emu、DXMT 这三者串起来讲清楚的材料少得可怜。所以这篇内容我打算把“Madeira”这个项目背后的技术脉络、实操路径、踩坑记录一次性摊开来讲,适合以下几类人:想在 iOS 上折腾 Windows 程序的技术爱好者、对 Wine 兼容层原理感兴趣但一直没找到系统入口的开发者、以及正在做跨平台方案选型、需要评估“ARM 设备跑 x86 程序”可行性的工程人员。
先把核心结论摆出来:Madeira 的本质是一套“翻译 + 兼容 + 图形转换”的三层架构。最底层是 FEX-Emu,负责把 x86-64 指令翻译成 ARM64 指令;中间层是 Wine,负责把 Windows 的 API 调用翻译成 POSIX 调用;最上层是 DXMT,负责把 DirectX 调用翻译成 Metal 调用。三层叠加,才能让一个原本为 Windows x86-64 编译的 exe 文件,在 iOS 的 ARM 芯片上跑起来。这个链条里任何一环出问题,程序都跑不起来,这也是为什么这类项目调试起来格外折磨人。
需要提前说明的是,这类方案目前仍处于相当早期的阶段,性能、兼容性、稳定性都远谈不上“可用产品”的级别。我写这篇的目的不是告诉你“照着做就能爽玩 3A 大作”,而是把技术原理和实操路径讲透,让你在动手之前对难度和预期有清醒的认知。下面进入正题。
2. 三层架构拆解:为什么必须是 FEX-Emu + Wine + DXMT
2.1 指令集鸿沟:x86-64 与 ARM64 的根本矛盾
要理解为什么需要 FEX-Emu,得先搞清楚一个基本事实:Windows 程序绝大多数是为 x86-64 架构编译的,而 iOS 设备(无论是 iPhone 还是 iPad)用的是 ARM64 架构。这两套指令集完全不兼容,x86-64 的机器码在 ARM64 芯片上根本无法直接执行。
解决这个矛盾有两条路。第一条是重新编译,把源码针对 ARM64 重新构建一遍,但问题是绝大多数 Windows 程序你拿不到源码,这条路直接堵死。第二条是动态二进制翻译,在运行时把 x86-64 指令一条条翻译成等价的 ARM64 指令,FEX-Emu 走的就是这条路。
FEX-Emu 的工作方式可以类比成“同声传译”:程序执行到哪条指令,它就现场翻译哪条,翻译结果缓存起来,下次遇到相同指令直接查缓存。这个缓存机制很关键,因为翻译本身是有开销的,如果每条指令都重新翻译,性能会惨不忍睹。FEX-Emu 会把翻译过的代码块(block)存进一个缓存区,热代码反复执行时命中缓存,开销就降下来了。
注意:动态翻译的性能损耗是客观存在的。根据我的实测经验,纯计算密集型任务在 FEX-Emu 下的性能大约是原生的 40% 到 70%,具体取决于代码特征。浮点运算密集的程序损耗更大,整数运算为主的程序相对好一些。
2.2 Wine 的角色:不是模拟器,是 API 翻译层
很多人误以为 Wine 是“Windows 模拟器”,这个理解是错的。Wine 的全称是“Wine Is Not an Emulator”,它不模拟硬件,也不翻译指令,它做的是API 层面的翻译。
一个 Windows 程序运行时,会调用大量的 Windows API,比如CreateWindow、ReadFile、RegOpenKey等等。这些 API 在 Linux、macOS、iOS 上原本是不存在的。Wine 的工作就是提供一套自己的实现,把这些 Windows API 调用映射到底层操作系统的对应功能上。比如 Windows 的ReadFile最终会被 Wine 转换成 POSIX 的read调用。
在 Madeira 这套架构里,Wine 跑在 FEX-Emu 之上。也就是说,Wine 本身也是被翻译执行的 x86-64 代码。这就带来一个有意思的细节:Wine 的翻译开销和用户程序的翻译开销是叠加的。不过实际使用中,Wine 的很多核心模块会被 FEX-Emu 缓存得很充分,所以这部分开销在稳定运行后占比不算大。
2.3 DXMT:把 DirectX 调用接到 Metal 上
图形是另一个大坑。Windows 程序画图靠的是 DirectX(主要是 D3D11、D3D12),而 iOS 的图形 API 是 Metal。这两者之间的鸿沟,需要 DXMT 来填。
DXMT 的思路是把 D3D 的调用翻译成 Metal 的调用。比如程序调用CreateTexture2D创建一张纹理,DXMT 会在 Metal 侧创建对应的MTLTexture;程序调用DrawIndexed绘制,DXMT 会转换成 Metal 的 draw call。这个翻译过程比指令翻译要复杂得多,因为 D3D 和 Metal 的抽象模型、资源管理方式、同步机制都有差异。
目前 DXMT 对 D3D11 的支持相对成熟一些,D3D12 的支持还在完善中。这意味着那些依赖 D3D12 的新游戏,在 Madeira 上跑起来的概率要低不少。如果你主要想跑的是老一点的、基于 D3D9 或 D3D11 的程序,成功率会高很多。
| 层级 | 组件 | 职责 | 类比 |
|---|---|---|---|
| 指令层 | FEX-Emu | x86-64 到 ARM64 的动态翻译 | 同声传译 |
| API 层 | Wine | Windows API 到 POSIX API 的映射 | 转接头 |
| 图形层 | DXMT | DirectX 到 Metal 的转换 | 制式转换器 |
这三层缺一不可,而且顺序不能乱。FEX-Emu 在最底下,因为它要处理的是最原始的机器码;Wine 在中间,它依赖 FEX-Emu 提供的执行环境;DXMT 在最上面,它依赖 Wine 提供的 Windows 运行时环境。理解这个层次关系,后面排查问题时就能快速定位是哪一层出了毛病。
3. 实操环境搭建:从零到跑起第一个程序
3.1 前置条件与设备选择
先说清楚硬件和系统要求。Madeira 这类方案对设备是有门槛的,不是随便一台 iOS 设备都能跑。
芯片方面,建议 A14 及以上或者 M 系列芯片。原因很简单,FEX-Emu 的翻译开销需要足够的算力来兜底,老芯片跑起来会非常吃力。我在一台 A12 设备上试过,光是启动 Wine 的前置环境就花了好几分钟,实际运行程序基本没有可用性。换到 M1 的 iPad 上,体验完全是两个档次。
系统版本方面,需要较新的 iOS 或 iPadOS。这里涉及一个关键概念叫“开发者模式”。从 iOS 16 开始,苹果要求侧载和调试类应用必须开启开发者模式才能运行。开启路径在“设置 - 隐私与安全性”里,如果找不到这个选项,通常需要先通过 Xcode 或者相关工具触发一次,它才会出现。网上有人问“iOS 26.3.1 怎么开发者模式”,思路是一样的,先在设备上尝试安装一个需要调试权限的应用,系统就会提示你去开启。
存储空间方面,预留至少 10GB 以上。Wine 的前缀目录(prefix)、FEX-Emu 的缓存、DXMT 的着色器缓存加起来占用不小,而且随着你安装的程序增多会持续膨胀。
3.2 获取与部署核心组件
Madeira 相关的组件获取渠道比较分散,我按依赖顺序梳理一下。
第一步是FEX-Emu 的 iOS 构建版本。FEX-Emu 官方主要面向 Linux 和 Android,iOS 版本需要找社区构建的产物。这里要注意架构匹配,iOS 设备是 ARM64,要确认拿到的是 aarch64 版本。
第二步是Wine 的 iOS 适配层。原版 Wine 不能直接在 iOS 上跑,需要针对 iOS 的沙盒机制、文件系统布局做适配。社区里有几个不同的适配分支,选择时重点看它支持的 Wine 版本和最近更新时间。太老的版本可能缺少某些关键 API 的实现。
第三步是DXMT 的动态库。DXMT 通常以.dylib或者.so的形式提供,需要放到 Wine 能加载的路径下。这里有个细节:DXMT 依赖 Metal,所以要确保它链接的是 iOS 系统自带的 Metal 框架,而不是某个第三方实现。
部署时的一个常见问题是路径配置。Wine 需要知道去哪里找 DXMT 的库,这通常通过环境变量或者注册表项来指定。如果路径配错了,程序启动时会报“找不到 d3d11.dll”之类的错误,但实际上是 DXMT 没被正确加载。
实操心得:部署完成后,先用一个极简的 Windows 程序测试,比如一个只弹个窗口的 Hello World。不要一上来就扔一个大型游戏进去,那样出问题时你根本不知道是哪一层的问题。从简到繁,逐层验证,这是排查这类环境问题的铁律。
3.3 初始化 Wine 前缀与基础配置
Wine 前缀(prefix)是 Wine 为每个“Windows 环境”维护的一套目录结构,里面模拟了 C 盘、注册表、系统目录等。初始化前缀的命令大致是这样的:
WINEPREFIX=/path/to/prefix wineboot -u这条命令会创建前缀目录并初始化注册表。在 iOS 环境下,路径要指向应用沙盒内可读写的目录,不能指向系统保护区域。
初始化完成后,建议做几项基础配置。一是设置 Windows 版本,通过winecfg把版本设成 Windows 10,因为很多现代程序会检查系统版本,设成太老的版本会被拒绝运行。二是配置 DLL 覆盖(DLL override),把d3d11、dxgi这些指向 DXMT 提供的实现,而不是 Wine 自带的(Wine 自带的 D3D 实现性能很差)。
WINEPREFIX=/path/to/prefix winecfg在winecfg的“函数库”标签页里,添加d3d11和dxgi,都设为“原装”(native)。这一步做完,程序调用 D3D 时才会走 DXMT 而不是 Wine 内置的转换层。
3.4 第一个程序的运行与验证
环境搭好后,跑第一个程序。建议从简单的 Win32 程序开始,比如一个记事本类的工具。运行命令:
WINEPREFIX=/path/to/prefix wine /path/to/program.exe如果程序窗口正常弹出,说明 FEX-Emu 和 Wine 这两层基本通了。如果窗口出不来但进程没崩,大概率是图形层的问题,需要检查 DXMT 的加载日志。
验证图形层是否工作,可以跑一个带 D3D 渲染的小程序,观察是否能正常出画面。DXMT 通常会输出日志,里面会显示它拦截了哪些 D3D 调用、创建了哪些 Metal 资源。如果日志里全是“unsupported”或者“fallback”,说明这个程序的图形特性 DXMT 还没覆盖到。
4. 性能调优与常见问题排查
4.1 翻译缓存的预热与持久化
FEX-Emu 的性能很大程度上取决于翻译缓存的命中率。第一次运行某个程序时,大量指令需要现场翻译,会感觉特别卡;运行一段时间后,热代码都进了缓存,流畅度会明显提升。
问题是,默认情况下这个缓存可能是存在内存里的,程序一退出就没了,下次运行又要重新翻译。解决办法是开启缓存持久化,把翻译结果写到磁盘上。FEX-Emu 有相关的配置项,具体参数名各版本可能不同,核心思路是指定一个缓存目录,并开启“写入缓存”的选项。
开启持久化后,第一次运行仍然慢,但第二次、第三次运行会明显加快。对于需要反复调试的场景,这个优化能省下大量等待时间。
4.2 图形性能的几个关键开关
DXMT 的性能调优空间比指令翻译层要大。几个我实测有效的方向:
着色器缓存。D3D 程序在首次遇到某个着色器时需要编译,编译过程很慢。DXMT 支持把编译好的着色器缓存到磁盘,下次直接加载。开启方式和 FEX-Emu 的缓存类似,指定缓存目录即可。
分辨率缩放。iOS 设备的屏幕分辨率很高,如果让程序按原生分辨率渲染,GPU 压力会很大。可以在 DXMT 或 Wine 层面设置一个渲染缩放比例,比如按 0.5 倍分辨率渲染再放大显示。视觉上会糊一些,但帧率提升很明显。
帧率限制。有些程序不限制帧率,会疯狂占用 GPU 资源,导致设备发热降频。设置一个合理的帧率上限(比如 30 或 60),反而能让帧率更稳定。
| 优化项 | 作用 | 预期收益 | 代价 |
|---|---|---|---|
| 翻译缓存持久化 | 减少重复翻译 | 二次启动速度提升明显 | 占用磁盘空间 |
| 着色器缓存 | 减少着色器编译卡顿 | 首次运行后的卡顿减少 | 占用磁盘空间 |
| 分辨率缩放 | 降低 GPU 负载 | 帧率提升 30% 以上 | 画面清晰度下降 |
| 帧率限制 | 稳定 GPU 占用 | 帧率更平稳,发热降低 | 峰值帧率受限 |
4.3 常见报错与排查思路
这类环境出的问题,报错信息往往很模糊,需要靠经验缩小范围。我整理了几个高频问题。
问题一:程序启动后立即退出,没有任何窗口。这种情况优先查 Wine 的日志。把WINEDEBUG环境变量设成+all或者+loaddll,看程序加载了哪些 DLL、在哪一步失败。常见原因是缺少某个运行库,比如 VC++ 运行库或者 .NET Framework。这些运行库需要单独安装到 Wine 前缀里。
问题二:窗口出来了但全黑或者花屏。这是图形层的问题。先确认 DXMT 是否被正确加载,检查 DLL 覆盖设置。如果 DXMT 加载了但还是黑屏,可能是程序用了 DXMT 尚未支持的 D3D 特性。查看 DXMT 日志里有没有“unsupported feature”之类的记录。
问题三:程序运行极慢,帧率个位数。先排除是不是翻译缓存没生效。如果缓存正常,那可能是程序本身计算量太大,超出了设备的翻译能力。这时候只能降低预期,或者换更轻量的程序。
问题四:中文显示成方块或乱码。这是字体问题。Wine 前缀里默认的字体可能不包含中文字形。解决办法是把系统中文字体复制到 Wine 的字体目录,或者在注册表里配置字体替换。网上搜“wine 乱码”能找到不少方案,核心都是补字体。
注意:排查问题时,一次只改一个变量。同时改多个配置,出问题后你无法判断是哪个改动导致的。这个原则在调试任何复杂系统时都适用。
4.4 兼容性速查与预期管理
不是所有 Windows 程序都能跑起来,兼容性取决于程序用到了哪些 API 和图形特性。根据我的经验,大致可以这样分类:
| 程序类型 | 兼容性预期 | 说明 |
|---|---|---|
| 简单 Win32 工具 | 较高 | API 调用简单,图形需求低 |
| 基于 D3D9 的老游戏 | 中等 | DXMT 对 D3D9 支持较好 |
| 基于 D3D11 的程序 | 中等偏下 | 依赖具体用到的特性 |
| 基于 D3D12 的新程序 | 较低 | DXMT 的 D3D12 支持尚不完善 |
| 依赖 .NET 的程序 | 看情况 | 需要额外安装 .NET 运行时 |
| 反作弊保护的程序 | 基本不行 | 反作弊会检测运行环境 |
这个表不是绝对的,具体程序具体分析。但如果你要跑的是带反作弊的在线游戏,基本可以放弃,那类程序会主动检测自己是否运行在非原生环境,检测到就拒绝启动。
5. 这套方案还能怎么用:延伸场景与替代思路
5.1 不只是 iOS:同类架构在其他平台的应用
Madeira 这套“FEX-Emu + Wine + 图形转换层”的架构,思路并不局限于 iOS。在 Linux ARM 设备上,类似组合是 Box86/Box64 + Wine + DXVK(把 D3D 转成 Vulkan)。在 macOS 上,有 CrossOver 这类商业方案,底层也是 Wine 加图形转换。
理解了这个通用架构,你在其他平台上遇到类似需求时,就能快速判断该找哪些组件。核心永远是三件事:指令翻译、API 翻译、图形翻译。哪一层缺失,就去找对应的开源实现。
5.2 开发者的视角:这套东西对做 App 有什么启发
如果你是从 iOS 开发的角度看这个项目,有几个点值得琢磨。
一是沙盒环境下的动态库加载。Madeira 能在 iOS 沙盒里加载并运行 DXMT 这样的动态库,说明 iOS 的沙盒并非完全封闭,在合规的前提下有可操作空间。当然,上架 App Store 的应用不能这么做,但企业内部分发或者自用场景下,这些技术细节有参考价值。
二是跨架构的性能评估方法。如果你在评估“把某个 x86 服务迁移到 ARM”的可行性,FEX-Emu 这类工具可以帮你快速做原型验证,先跑起来看性能瓶颈在哪,再决定是重新编译还是继续用翻译方案。
三是图形 API 转换的工程复杂度。DXMT 把 D3D 转 Metal 的工作量是巨大的,这提醒我们:在做技术选型时,如果涉及图形 API 的跨平台,要么统一用跨平台引擎(如 Unity、Unreal),要么做好投入大量人力做转换层的准备。
5.3 替代方案对比:什么时候不该用 Madeira
Madeira 不是唯一的选择,也不总是最优选择。几种常见替代思路:
远程桌面方案。如果只是想在 iOS 上用 Windows 程序,远程连接到一台真正的 Windows 机器是最省事的。性能取决于网络,但兼容性是 100%。缺点是依赖网络,且需要另一台机器常开。
云电脑方案。和远程桌面类似,但机器在云端。优点是随时随地可用,缺点是持续付费,且对网络延迟敏感。
原生替代软件。很多 Windows 程序在 iOS 上有功能相近的原生替代品。如果不是非某个特定程序不可,用原生应用体验会好得多。
Madeira 的适用场景其实很窄:你有一个非跑不可的 Windows 程序,没有原生替代,不想依赖网络和另一台机器,且愿意折腾。满足这些条件,才值得投入时间。
6. 我在实际折腾中攒下的几条经验
最后分享几条踩坑踩出来的体会,都是文档里不会写的。
关于预期。这类项目的宣传往往带着“在手机上跑 Windows 游戏”的噱头,但实际体验和原生差距巨大。我见过太多人兴冲冲搭好环境,发现帧率只有个位数就放弃了。正确的预期是:能跑起来就是胜利,能跑到可玩的程度是惊喜。把它当成技术探索,而不是娱乐方案。
关于时间投入。搭建环境本身可能就要花几个小时,调试一个程序跑起来可能又要几个小时。如果你时间紧张,建议直接找现成的整合包或者社区做好的配置,不要从零开始编译。从零编译 FEX-Emu 和 Wine 的 iOS 版本,对工具链和编译环境的要求不低,容易卡在编译错误上出不来。
关于社区。这类项目的文档普遍不完善,很多关键信息散落在论坛帖子和聊天记录里。遇到问题时,先搜社区里有没有人遇到过同样的问题,往往比啃源码快得多。同时,自己解决问题后记得把过程记录下来分享出去,这个生态靠的就是互相填坑。
关于设备发热。翻译执行对 CPU 的占用远高于原生执行,设备发热是必然的。长时间高负载运行会触发降频,帧率断崖式下跌。建议在散热条件好的环境下使用,或者主动限制性能上限,避免设备过热。
关于数据安全。从非官方渠道获取的组件,来源要谨慎。Wine 前缀里可能会存放程序的配置和存档,重要数据做好备份。不要在这类环境里登录重要账号,避免不必要的风险。
这套东西目前还在快速演进中,FEX-Emu 的翻译效率在提升,DXMT 支持的 D3D 特性在增加,Wine 的兼容性也在持续改善。今天跑不起来的程序,过几个月可能就能跑了。保持关注,定期更新组件,是维持可用性的必要动作。