
1. 从“Madeira”这个名字说起它到底想解决什么问题第一次看到“Madeira”这个项目标题加上旁边那一串热搜词——FEX-Emu、Wine、DXMT、iOS、x86-64——我脑子里第一反应是这大概率是一个跟“跨架构运行 Windows 程序”相关的整合方案。为什么这么判断因为 FEX-Emu 是做 x86-64 指令翻译的Wine 是做 Windows API 转译的DXMT 是把 Direct3D 映射到 Metal 的而 iOS 又恰好是 Apple 生态里唯一一个既封闭又对图形性能有强需求的平台。把这几个词拼在一起指向非常明确在 ARM 架构的 Apple 设备上跑起原本为 x86-64 Windows 编译的游戏或应用。“Madeira”本身是一种酒的名字也常被用作项目代号。从命名习惯来看这类项目喜欢用酒名、地名或者某种隐喻来暗示“混合”“调和”的意味——把不同架构、不同系统、不同图形 API 调和到一起这本身就是一种“调制”。所以我在理解这个标题时不会把它当成一个单纯的模拟器而是一个兼容层整合项目它把指令翻译、系统调用转译、图形接口映射这三层能力打包在一起目标是在 iOS 或类似 ARM 平台上提供一个相对完整的 Windows 应用运行环境。那它解决了什么问题简单说就是“我想在 iPad 或者 iPhone 上玩某个只有 Windows 版的游戏但 App Store 里没有我也不想越狱装一堆乱七八糟的东西”。传统做法要么是串流依赖网络和另一台电脑要么是找已经移植好的版本数量极少要么就是自己折腾一堆工具链但根本跑不起来。Madeira 这类项目的价值就在于它试图把这条链路标准化你不需要分别去配 FEX-Emu、Wine 和 DXMT而是有一个统一的入口和配置流程。适合谁看如果你是一个喜欢在移动设备上折腾老游戏或者独立游戏的玩家或者你是一个对跨架构兼容层感兴趣、想了解 Wine 和 FEX-Emu 怎么配合的开发者再或者你只是好奇“ARM 上跑 x86 Windows 程序”这件事到底怎么实现的那这篇内容都值得往下看。我会尽量把原理讲清楚同时给出可操作的步骤和踩坑记录而不是只停留在概念层面。2. 核心组件拆解FEX-Emu、Wine、DXMT 各自扮演什么角色2.1 FEX-Emu把 x86-64 指令翻译成 ARM64 能听懂的话FEX-Emu 是一个用户态的 x86-64 到 ARM64 的二进制翻译器。它的工作方式不是像 QEMU 那样做全系统模拟而是只翻译用户态指令系统调用还是走宿主系统的。这样做的好处是性能损耗相对小而且不需要模拟整个硬件环境。你可以把它理解成一个“实时翻译官”x86-64 程序说一句话它立刻翻译成 ARM64 指令让 CPU 执行。在 Madeira 这个场景里FEX-Emu 负责的是最底层的指令翻译。因为 iOS 设备全是 ARM 架构而 Windows 游戏绝大多数是 x86-64 编译的没有这一层后面 Wine 和 DXMT 根本无从谈起。FEX-Emu 的配置重点在于 rootfs 的选择和 thunk 机制——它需要一套 x86-64 的库文件来支撑被翻译的程序同时又要能回调宿主系统的原生库来避免重复实现。注意FEX-Emu 对 x86-64 指令集的覆盖度很高但并不是 100%。某些依赖特定指令集扩展比如 AVX-512的程序可能会出问题。实际使用中大部分游戏和常用软件都能跑但遇到崩溃时首先要怀疑的就是指令翻译层。2.2 Wine把 Windows API 调用转成 POSIX 调用Wine 大家应该不陌生它的全称是“Wine Is Not an Emulator”虽然名字这么说但它确实在做一层“转译”——把 Windows 的 API 调用翻译成宿主系统的 POSIX 调用。在 Madeira 里Wine 运行在 FEX-Emu 之上也就是说Wine 本身也是 x86-64 的程序被 FEX-Emu 翻译后运行。这听起来有点绕x86-64 的 Wine 被翻译成 ARM64 执行然后 Wine 再去加载 x86-64 的 Windows 程序同样被翻译。这种嵌套结构的好处是Wine 的代码不需要为 ARM 重新编译直接拿现成的 x86-64 版本就能用。坏处是性能损耗会叠加而且调试起来更复杂。不过从实际效果来看对于很多 2D 游戏或者对性能要求不高的应用这套方案是可行的。Wine 的配置重点在于 prefix 的创建和 DLL 的覆盖。你需要一个干净的 Wine prefix然后根据目标程序的需求用winetricks或者手动替换某些 DLL。比如很多游戏需要d3dx9、vcrun2019这些运行库提前装好能省很多事。2.3 DXMT把 Direct3D 调用映射到 MetalDXMT 是一个相对较新的项目它的目标是把 Direct3D 11 和部分 Direct3D 12 的调用翻译成 Metal。为什么是 Metal因为 iOS 和 macOS 上Metal 是官方推荐的图形 API性能最好兼容性也最可控。相比之下用 OpenGL 或者 Vulkan 转译层在 Apple 平台上往往会遇到更多限制。在 Madeira 的架构里DXMT 是图形链路的最后一环。游戏调用 Direct3DWine 把调用转给 DXMTDXMT 再转成 Metal 命令提交给 GPU。这条链路每一层都有开销但好在 Metal 本身效率高所以整体表现比想象中好。DXMT 的配置重点在于版本匹配和着色器缓存——不同版本的 DXMT 对 D3D 特性的支持程度不一样有些游戏需要特定版本才能正常渲染。2.4 三者如何协同一张表看清分工组件负责层级输入输出关键配置FEX-Emu指令翻译x86-64 指令ARM64 指令rootfs、thunk 库WineAPI 转译Windows API 调用POSIX 调用prefix、DLL 覆盖DXMT图形映射Direct3D 调用Metal 命令版本、着色器缓存这三层缺一不可。没有 FEX-Emux86-64 程序根本跑不起来没有 WineWindows 程序找不到熟悉的 API没有 DXMT游戏画面出不来。Madeira 的价值就在于把这三点串起来并且提供一个相对统一的配置入口。3. 实操环境准备从零开始搭建 Madeira 运行环境3.1 设备与系统要求首先说清楚这套方案主要面向 Apple Silicon 设备M 系列芯片的 iOS 或 iPadOS 环境。为什么强调 Apple Silicon因为 FEX-Emu 的 ARM64 后端是针对 Apple 芯片优化的在 A 系列芯片上虽然也能跑但性能和兼容性会打折扣。系统版本建议 iOS 16 以上因为 Metal 的一些特性在旧版本上支持不完整。存储空间方面一个完整的 Wine prefix 加上 FEX-Emu 的 rootfs轻松占用 5GB 以上。如果你要装几个大游戏建议预留 20GB 以上。内存方面iPad Pro 的 8GB 或 16GB 版本会舒服很多4GB 的设备跑一些轻量应用还行大型游戏会比较吃力。提示在 iOS 上运行这类环境通常需要借助一些允许执行外部代码的工具或容器。具体方式因设备而异这里不展开讨论平台相关的安装细节重点放在环境配置本身。3.2 获取 FEX-Emu 和 rootfsFEX-Emu 的 rootfs 是一个包含 x86-64 基础库的文件系统镜像。你需要根据目标程序的需求选择合适的 rootfs 版本。一般来说Ubuntu 20.04 或 22.04 的 x86-64 rootfs 是比较稳妥的选择因为 Wine 在这些版本上测试得比较多。获取方式通常是从 FEX-Emu 的官方发布渠道下载对应的 rootfs 压缩包然后解压到指定目录。解压后你需要检查几个关键文件是否存在/lib/x86_64-linux-gnu/libc.so.6、/usr/lib/x86_64-linux-gnu/libstdc.so.6等。如果这些基础库缺失后面 Wine 启动会直接报错。# 假设 rootfs 解压在 /opt/fex-rootfs ls /opt/fex-rootfs/lib/x86_64-linux-gnu/libc.so.6 ls /opt/fex-rootfs/usr/lib/x86_64-linux-gnu/libstdc.so.6如果路径不对你需要调整 FEX-Emu 的配置指向正确的 rootfs 路径。这个路径通常在 FEX 的配置文件或者环境变量里指定。3.3 安装和配置 WineWine 的安装有两种方式一种是直接用系统包管理器安装另一种是下载预编译的 x86-64 Wine 二进制包。在 Madeira 场景下我建议用后者因为你需要的是 x86-64 版本的 Wine而不是 ARM64 原生版本。ARM64 原生 Wine 虽然性能更好但它无法直接运行 x86-64 的 Windows 程序除非你配合其他转译层那样配置会更复杂。下载 x86-64 Wine 后解压到一个目录比如/opt/wine-x86_64。然后设置环境变量export WINEARCHwin64 export WINEPREFIX/opt/wineprefix export PATH/opt/wine-x86_64/bin:$PATH第一次运行winecfg会初始化 prefix这个过程会创建一堆注册表和目录结构。初始化完成后你可以用winetricks安装一些常用运行库。winetricks是一个脚本它会自动下载并安装各种 Windows 组件省去手动替换 DLL 的麻烦。winetricks -q corefonts vcrun2019 d3dx9这几个组件覆盖了大部分游戏的基本需求corefonts解决字体乱码问题vcrun2019提供 Visual C 运行库d3dx9提供 Direct3D 9 的辅助库。注意-q参数是静默安装避免弹出一堆窗口。3.4 配置 DXMTDXMT 的配置相对简单但版本选择很关键。你需要根据目标游戏使用的 Direct3D 版本选择对应的 DXMT 版本。比如一个只用到 D3D11 的游戏用最新的 DXMT 通常没问题但如果游戏用了 D3D12 的某些高级特性可能需要特定版本才能正常渲染。DXMT 通常以 DLL 的形式提供你需要把这些 DLL 放到 Wine prefix 的system32和syswow64目录下然后在 Wine 的配置里设置 DLL 覆盖让 Wine 优先加载 DXMT 的 DLL 而不是自带的。# 假设 DXMT 的 DLL 在 /opt/dxmt/lib cp /opt/dxmt/lib/*.dll $WINEPREFIX/drive_c/windows/system32/然后在winecfg的 Libraries 标签页里把d3d11、dxgi等设置为 native 优先。这一步很关键如果设置错了游戏可能会黑屏或者直接崩溃。4. 实际运行与调试从启动到可玩的完整流程4.1 启动一个简单程序验证环境在跑游戏之前先用一个简单的 Windows 程序验证整个链路是否通畅。我一般会用notepad.exe或者一个小的测试工具。启动命令很简单wine notepad.exe如果记事本窗口能正常弹出说明 FEX-Emu、Wine 和基础图形链路都没问题。如果报错根据错误信息逐层排查如果是cannot execute binary file说明 FEX-Emu 没配置好如果是wine: cannot find Lxxx说明 Wine prefix 有问题如果是窗口出来了但花屏那可能是 DXMT 或者 Metal 层的问题。注意第一次启动 Wine 程序会比较慢因为 FEX-Emu 需要翻译大量指令而且着色器也需要编译。耐心等一会儿不要以为卡死了就强制退出。4.2 运行游戏并观察日志验证环境没问题后就可以尝试运行目标游戏了。启动命令通常是wine /path/to/game.exe这时候要盯着终端输出Wine 和 DXMT 会打印很多调试信息。重点关注几类信息err:开头的错误、fixme:开头的未实现功能、以及 DXMT 的渲染后端初始化信息。如果游戏窗口出来了但画面异常可以尝试设置 DXMT 的日志级别来获取更详细的信息。export DXMT_LOG_LEVELdebug wine /path/to/game.exe 21 | tee game.log把日志保存下来方便后续分析。很多问题在日志里都有线索比如缺少某个 DLL、着色器编译失败、或者 Metal 纹理格式不支持。4.3 性能调优的几个关键参数性能调优主要围绕三个方面FEX-Emu 的翻译缓存、Wine 的图形设置、以及 DXMT 的渲染选项。FEX-Emu 有一个翻译缓存机制第一次运行程序时会把翻译后的代码缓存起来后续启动会快很多。确保缓存目录有足够的空间并且没有被清理掉。你可以通过环境变量控制缓存大小和位置。Wine 这边可以关闭一些不必要的调试输出减少开销。在winecfg里把图形设置调整为“模拟虚拟桌面”有时候能解决一些窗口管理的问题但也会增加一点开销看情况使用。DXMT 这边着色器缓存是关键。确保缓存目录可写并且定期清理旧的缓存文件避免缓存过大导致加载变慢。另外如果游戏支持可以尝试降低分辨率或者关闭一些特效毕竟翻译层的开销摆在那里原生性能没法比。调优项推荐设置作用FEX 翻译缓存开启分配 2GB 以上减少重复翻译开销Wine 虚拟桌面按需开启解决窗口显示问题DXMT 着色器缓存开启定期清理加速二次启动游戏分辨率720p 或 1080p降低 GPU 负载4.4 常见崩溃场景与应对实际跑游戏时崩溃是家常便饭。我整理了几种典型情况第一种是启动即崩溃没有任何窗口。这种通常是缺少运行库或者 DLL 覆盖设置错误。检查winetricks是否装全了以及winecfg里的 DLL 覆盖是否正确。第二种是窗口出现但黑屏。这多半是 DXMT 的问题可能是版本不匹配或者着色器编译失败。尝试换一个 DXMT 版本或者删除着色器缓存重新编译。第三种是玩了几分钟突然崩溃。这种可能是内存不足或者翻译层遇到了不支持的指令。查看日志里最后几条输出通常能找到线索。如果是内存问题尝试降低游戏画质或者关闭后台应用。第四种是音频异常或者没有声音。Wine 的音频驱动在 iOS 上可能需要额外配置检查winecfg的音频设置尝试切换不同的驱动模式。5. 常见问题速查与避坑经验5.1 Wine 乱码问题怎么解决Wine 乱码是高频问题主要表现为中文显示成方块或者问号。根本原因是缺少中文字体或者字体映射不对。解决办法分两步第一步安装中文字体到 Wine 的字体目录第二步在注册表里设置字体替换。# 把中文字体复制到 Wine 字体目录 cp /path/to/simsun.ttc $WINEPREFIX/drive_c/windows/Fonts/然后创建一个注册表文件把默认的字体替换成中文字体cat font.reg EOF REGEDIT4 [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes] ArialSimSun TahomaSimSun EOF wine regedit font.reg这样大部分中文乱码问题都能解决。如果还有个别程序乱码可能是该程序自带了字体或者用了特殊的字体渲染方式需要单独处理。5.2 FEX-Emu 报“不支持的指令”怎么办FEX-Emu 虽然覆盖了大部分 x86-64 指令但总有一些边角情况。如果日志里出现Unsupported instruction或者类似的错误说明程序用到了 FEX 还没实现的指令。这时候有几个选择一是升级 FEX-Emu 到最新版本看看是否已经支持二是尝试用不同的 rootfs因为不同 rootfs 里的库可能编译选项不同避开了某些指令三是如果程序有 32 位版本可以尝试用 32 位模式运行FEX 对 32 位的支持有时候反而更成熟。提示遇到不支持的指令时不要急着放弃。先去 FEX-Emu 的 issue 列表里搜一下很可能已经有人遇到并提供了 workaround。5.3 DXMT 渲染异常排查思路DXMT 渲染异常的表现很多花屏、纹理错乱、阴影异常、帧率骤降等。排查思路是从简到繁先确认 DXMT 版本是否匹配游戏的 D3D 版本然后检查着色器缓存是否损坏尝试删除缓存重新编译再然后查看 DXMT 日志里是否有 Metal 相关的错误比如纹理格式不支持、渲染目标创建失败等。如果问题依旧可以尝试在 DXMT 配置里关闭一些高级特性比如异步着色器编译、多线程渲染等。这些特性在桌面平台上通常没问题但在 iOS 的 Metal 实现上可能触发一些边界情况。5.4 性能不如预期的优化方向性能不如预期时先别急着怪翻译层。用工具看一下瓶颈在哪里是 CPU 翻译开销大还是 GPU 渲染慢还是内存带宽不够。FEX-Emu 和 DXMT 都有性能统计输出开启后可以看到各阶段的耗时。如果瓶颈在 CPU 翻译可以尝试调整 FEX 的翻译块大小和缓存策略。如果瓶颈在 GPU降低渲染分辨率或者关闭一些后处理特效通常有效。如果瓶颈在内存减少同时运行的程序或者增加交换空间。问题现象可能原因排查方向启动即崩溃缺少运行库检查 winetricks 安装黑屏无画面DXMT 版本不匹配更换 DXMT 版本中文乱码字体缺失安装中文字体并设置替换帧率低CPU 翻译开销大调整 FEX 缓存策略玩一会崩溃内存不足降低画质或关闭后台5.5 我踩过的几个坑第一个坑是 rootfs 权限问题。解压 rootfs 时如果权限不对Wine 启动时会报一堆权限错误。解决办法是解压后统一设置权限确保当前用户有读写执行权限。第二个坑是 DXMT 的 DLL 覆盖顺序。我一开始只覆盖了d3d11.dll忘了dxgi.dll结果游戏能启动但画面出不来。后来把两个都覆盖了才正常。所以 DLL 覆盖要成对设置不能只设一个。第三个坑是着色器缓存目录不可写。DXMT 默认的缓存目录在某些环境下没有写权限导致每次启动都重新编译着色器慢得让人崩溃。手动指定一个可写的缓存目录就解决了。第四个坑是 FEX-Emu 的 thunk 库版本不匹配。thunk 库是 FEX 用来回调宿主原生库的桥梁如果版本和 FEX 本体不匹配会出现各种奇怪的崩溃。解决办法是确保 thunk 库和 FEX 本体来自同一个发布版本。6. 这套方案还能怎么扩展Madeira 这个思路其实不局限于 iOS。同样的架构——FEX-Emu 做指令翻译、Wine 做 API 转译、DXMT 做图形映射——可以搬到任何 ARM64 的 Linux 设备上比如树莓派、某些 ARM 服务器、或者其他基于 ARM 的移动设备。区别只在于图形后端的适配在 Linux 上可以用 Vulkan 或者 OpenGL 替代 MetalDXMT 也有对应的 Vulkan 后端版本。另一个扩展方向是容器化。把整个环境打包成一个容器镜像用户只需要拉取镜像就能运行省去配置的麻烦。这对于分享和复现来说非常有价值尤其是当你想把配置好的环境发给朋友时容器化能保证环境一致性。还有就是自动化配置脚本。把前面提到的所有步骤——下载 rootfs、安装 Wine、配置 DXMT、设置字体——写成一个脚本用户只需要运行脚本就能完成大部分配置。这能大幅降低入门门槛也能减少因为手动操作导致的配置错误。我个人在实际操作中的体会是这类跨架构兼容方案最耗时的部分往往不是技术本身而是各种环境差异导致的配置问题。同样的步骤在不同的设备、不同的系统版本上结果可能完全不一样。所以如果你打算尝试建议先在一个干净的环境里从头走一遍把每一步都记录下来形成自己的配置清单。这样下次再搭环境时直接照着清单走能省很多时间。