1. 从“Madeira”说起这个项目到底在折腾什么第一次看到“Madeira”这个名字加上旁边一串 FEX-Emu、Wine、DXMT、iOS、x86-64 的热搜词我脑子里第一反应是这又是一个在“跨架构跑 Windows 程序”这条路上死磕的项目。事实也确实如此。Madeira 本质上是一套把 x86-64 的 Windows 应用搬到 ARM 架构设备尤其是 Apple Silicon 的 Mac、以及部分 iOS/iPadOS 环境上运行的兼容层方案组合。它不是一个单一软件而是一整套“翻译 兼容 图形转换”的流水线。我先把这套流水线用大白话讲清楚不然后面全是空中楼阁。x86-64 是传统 PC 的指令集ARM 是现在苹果 M 系列芯片、手机芯片用的指令集两者“语言不通”。FEX-Emu 干的事就是实时翻译把 x86-64 指令翻译成 ARM 能听懂的指令。但光翻译指令还不够Windows 程序还依赖一大堆 Windows 系统调用和 DLL这时候 Wine 上场它负责把 Windows 的 API 调用“翻译”成类 Unix 系统的调用。再往上Windows 游戏大量用 DirectX 渲染DXMT 就是把 DirectX 调用转成 Metal苹果的图形 API的桥。三层叠起来才能让一个 Windows 的 exe 在 Mac 上跑起来。那为什么标题叫 Madeira我的理解是这类项目喜欢用酒或者地名命名Wine 本身就是“葡萄酒”Madeira 是马德拉酒属于加强葡萄酒跟 Wine 一脉相承。这基本坐实了它和 Wine 生态的血缘关系。所以这篇博文我就围绕“Madeira 这套 x86-64 Windows 应用在 ARM 设备上的兼容方案”来展开把 FEX-Emu、Wine、DXMT 三者的分工、配置、踩坑点全部拆开讲。适合谁看适合那些手里有 M 系列 Mac、想跑一些 Windows 老软件或游戏、又不想装虚拟机的折腾党也适合做跨平台兼容、对指令翻译和图形转换感兴趣的技术人。需要提前说明的是下面涉及的具体配置和参数一部分来自社区常见实践一部分是我自己在类似方案里踩出来的经验我会明确标注哪些是“通用做法”、哪些是“我的补充推断”你照着抄之前先理解原理别盲抄。2. 三层架构拆解FEX-Emu、Wine、DXMT 各自管什么2.1 FEX-Emu把 x86-64 指令“同声传译”成 ARMFEX-Emu 是整个链条里最底层、也最硬核的一环。它的工作模式是动态二进制翻译Dynamic Binary TranslationDBT。你可以把它想象成一个同声传译员x86-64 程序每执行一条指令FEX 就把它翻译成等价的 ARM64 指令然后交给芯片执行。为了性能它不会傻乎乎地一条条翻而是会把常用的代码块缓存起来JIT 编译下次遇到同样的代码块直接调用缓存省去重复翻译的开销。这里有个关键概念叫“块”block。FEX 会把一段连续的 x86 指令识别成一个基本块翻译成一段 ARM 代码存进缓存。程序第一次跑某段逻辑会慢因为要翻译第二次跑就快了因为命中缓存。这也是为什么很多兼容层“第一次启动特别慢后面就顺了”的原因。FEX 的配置里有几个参数值得关注。一个是FEX_TSOENABLED控制是否启用 x86 的强内存序TSO模拟。x86 的内存模型比 ARM 严格很多老程序依赖这个严格顺序关掉它性能会好但可能出诡异 bug。我的建议是默认开着除非你明确知道程序不依赖强内存序。另一个是FEX_ROOTFS指定一个 x86-64 的根文件系统路径让 FEX 在里面找 x86 的库文件。这个路径配错了程序会报“找不到 xxx.so”非常典型。注意FEX-Emu 本身不提供 Windows 兼容能力它只负责指令翻译。你直接拿它跑 Windows exe 是跑不起来的必须配合 Wine。很多人第一次折腾就卡在这以为装了 FEX 就能跑 Windows 程序结果一脸懵。2.2 WineWindows API 的“翻译官”Wine 的定位是“Wine Is Not an Emulator”它不翻译指令只翻译 API。Windows 程序调用CreateWindowEx、ReadFile这些函数时Wine 把它们映射到对应的类 Unix 系统调用或自实现逻辑上。在 Madeira 这套方案里Wine 跑在 FEX 之上——也就是说Wine 本身也是被 FEX 翻译的 x86-64 程序它再去加载和运行目标 Windows 应用。这就带来一个有意思的层级关系FEX 翻译 WineWine 翻译 Windows 应用。两层翻译叠在一起性能损耗是必然的但换来的是不用虚拟机、不用双系统的轻量体验。Wine 的配置核心是WINEPREFIX也就是“酒瓶”目录。每个 prefix 是一个独立的 Windows 环境里面有注册表、C 盘目录、DLL 等。我的经验是不同软件用不同 prefix别全塞一个瓶子里。因为很多软件会往注册表里写全局配置互相污染出了问题极难排查。新建 prefix 的命令很简单WINEPREFIX~/.wine-madeira WINEARCHwin64 winecfg这条命令会创建一个 64 位的 prefix 并打开配置界面。WINEARCH一旦设定就不能改想换架构只能删了重建这点务必注意。关于热搜里出现的“wine 乱码”“wine 栏是乱码”这几乎是每个 Wine 用户都会遇到的经典问题。根因是字体缺失或 locale 不匹配。解决办法通常是两步第一把系统的中文字体比如思源黑体、文泉驿复制或链接到 prefix 的drive_c/windows/Fonts目录第二在winecfg的“显示”里把字体替换设置好或者直接改注册表把默认字体指向一个存在的中文字体。乱码问题九成出在这。2.3 DXMTDirectX 到 Metal 的图形桥梁图形是游戏能不能跑的关键。Windows 游戏用 DirectXD3D9/10/11/12苹果设备用 Metal。中间需要一个转换层。历史上这类方案有 DXVK转 Vulkan、MoltenVKVulkan 转 Metal而 DXMT 是直接做 DirectX 到 Metal 的转换省掉中间一层理论上延迟更低。DXMT 主要覆盖 D3D11 和部分 D3D12。它的工作方式是把 D3D 的着色器HLSL编译成 Metal 的着色器语言MSL把资源绑定、渲染状态映射到 Metal 的对应概念上。这里最容易出问题的是着色器编译有些游戏用了非常规的 HLSL 写法DXMT 编译不过去就会黑屏或者花屏。配置 DXMT 时通常需要设置环境变量告诉 Wine 用哪个 D3D 实现。比如export WINEDLLOVERRIDESd3d11n,b;dxgin,b这里的n,b意思是“优先用 native原生的 d3d11.dll其次用 builtin内置”。配合 DXMT 提供的 dll 文件放进 prefix 的system32目录就能让游戏走 DXMT 路径。如果游戏还是黑屏第一件事是看日志里有没有“shader compile failed”之类的字样有的话基本就是 DXMT 没吃下这个着色器。3. 从零搭一套 Madeira 环境的完整实操3.1 环境准备与依赖安装假设你在 Apple Silicon 的 Mac 上折腾这是目前最主流的场景。第一步是装 Homebrew这个不用多说。然后需要装 FEX-Emu。社区常见的做法是通过第三方 tap 或者自己编译。自己编译的话依赖包括 CMake、Ninja、LLVM 等编译一次大概要十几分钟到半小时取决于机器性能。brew install cmake ninja llvm git clone https://github.com/FEX-Emu/FEX.git cd FEX git submodule update --init --recursive cmake -DCMAKE_BUILD_TYPERelease -DENABLE_ASSERTIONSOFF -S . -B build ninja -C build编译完成后把build/Bin加入 PATH。接着装 Wine。这里有个坑不要用系统自带的或者 Homebrew 默认的 Wine因为那些通常是给 ARM 原生或者给 Intel Mac 准备的不一定适配 FEX 的 x86-64 翻译路径。更稳妥的做法是用专门为 FEX 场景准备的 Wine 构建或者用wine64的 x86-64 版本。DXMT 则需要从它的发布页下载对应的 dll 包解压后把d3d11.dll、dxgi.dll、d3d10core.dll等文件复制到你的 Wine prefix 的drive_c/windows/system32目录。复制前记得备份原有的同名 dll出问题好回滚。3.2 创建并配置 Wine Prefixprefix 是整个环境的“容器”配置好坏直接决定后续顺不顺。我一般会单独建一个目录比如~/.madeira-prefix然后export WINEPREFIX~/.madeira-prefix export WINEARCHwin64 wineboot -uwineboot -u会初始化 prefix创建 C 盘目录结构、注册表等。第一次跑会弹一些提示正常。初始化完成后先解决字体问题把中文字体拷进去cp /System/Library/Fonts/PingFang.ttc $WINEPREFIX/drive_c/windows/Fonts/然后打开winecfg在“显示”标签里把默认字体设成 PingFang 或者你拷进去的字体。这一步做完大部分中文乱码就没了。如果还有个别软件乱码那多半是它自己硬编码了某个字体名需要在注册表里做字体替换HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes。3.3 让 FEX 接管 Wine 的执行关键一步来了怎么让 FEX 去翻译 Wine而不是让系统直接跑 Wine。通常的做法是用 FEX 提供的FEXInterpreter或者FEXBash来启动 Wine。比如FEXBash -c wine your_app.exeFEXBash会启动一个被 FEX 翻译的 shell里面所有 x86-64 程序都会被 FEX 接管。这样 Wine 和它加载的 Windows 应用就都跑在翻译层上了。如果你直接wine your_app.exe那 Wine 会以 ARM 原生方式跑如果它是 ARM 版加载 x86-64 的 exe 就会失败。这里有个性能调优点FEX 的 JIT 缓存目录默认在~/.cache/fex-emu如果磁盘空间够别去清它缓存越热启动越快。另外可以设置FEX_TSOENABLED0试试性能但如前所述可能引入 bug建议先默认。3.4 图形层接入 DXMT 并验证图形这块先确认 DXMT 的 dll 已经就位然后设置 DLL 覆盖export WINEDLLOVERRIDESd3d11n,b;dxgin,b;d3d10coren,b启动游戏时加上日志输出方便排查FEXBash -c WINEDEBUGd3d11 wine game.exe 21 | tee game.log跑起来后看game.log如果看到 DXMT 的初始化信息说明图形层接上了。如果黑屏先看有没有 shader 编译错误如果花屏多半是纹理格式或渲染目标格式不匹配可以尝试在 DXMT 配置里调整格式映射。DXMT 一般有个配置文件比如dxmt.conf里面可以指定一些兼容性开关具体项随版本变化建议对照你下载的那个版本的文档。4. 常见问题与排查速查表折腾这套东西问题基本集中在几个地方。我把高频问题和排查思路整理成表方便你对照。现象可能原因排查方向启动即报“找不到 xxx.so”FEX_ROOTFS 路径不对或 x86 库缺失检查 FEX_ROOTFS 指向的根文件系统里是否有对应库Wine 界面中文全是方块字体缺失或字体替换没配拷中文字体进 Fonts 目录配 FontSubstitutes程序启动后闪退无日志可能是 TSO 内存序问题尝试开/关 FEX_TSOENABLED 对比游戏黑屏但有声音着色器编译失败或 D3D 未走 DXMT看日志有无 shader compile failed检查 DLL 覆盖游戏花屏/贴图错乱纹理格式映射不兼容调整 DXMT 格式配置或换 DXMT 版本性能极低卡成幻灯片JIT 缓存未热或 TSO 开销大多跑几次让缓存热起来评估关 TSOprefix 创建失败WINEARCH 与已有 prefix 冲突删掉旧 prefix 重建架构不能中途改除了表里的还有几个“独家”经验值得说。第一日志是你的命根子。WINEDEBUG可以按通道开比如d3d11、loaddll、module但别全开all日志会大到没法看。第二遇到诡异问题时先怀疑 prefix 污染。我遇到过某软件改了注册表导致另一个软件起不来最后发现是共用了 prefix。第三FEX 和 Wine 的版本要匹配社区里经常有“新版 FEX 配旧版 Wine 出问题”的情况升级时最好一起升或者锁定一套已知能用的版本组合。关于热搜里那些“wine 乱码”“麒麟 wine 助手”“统信 wine 兼容组件”之类的词其实反映的是同一个需求在非 Windows 系统上跑 Windows 程序字体和兼容组件是绕不开的坎。麒麟、统信这些国产系统上的 Wine 助手本质也是把 Wine 和一堆兼容配置打包好降低使用门槛。Madeira 这套方案在苹果生态里扮演的其实是类似的角色只不过它多了 FEX 这层指令翻译复杂度更高。5. 性能调优与进阶玩法5.1 让 JIT 缓存真正发挥作用FEX 的 JIT 缓存是性能的关键。默认情况下缓存会持久化到磁盘但有些配置下可能每次启动都重新翻译。你要确认FEX_JITCACHE或者相关环境变量指向了一个可写的持久目录。缓存热了之后第二次启动同一个程序会明显快很多。我的实测是一个中型游戏第一次启动可能要一两分钟大量翻译第二次可能二三十秒就进主界面了。另外FEX 支持多线程翻译FEX_MULTIBLOCK之类的选项随版本不同开启后能利用多核加速翻译过程。但多线程翻译有时会引入竞态如果开了之后程序不稳定就关掉。5.2 图形层的取舍DXMT 还是 DXVKMoltenVKDXMT 是直连 Metal理论上路径短、延迟低。但它的成熟度不如 DXVKMoltenVK 这条老路。有些游戏在 DXMT 下跑不起来换 DXVK 反而能跑。所以我的建议是先试 DXMT不行再试 DXVK。DXVK 需要 Vulkan而苹果上没有原生 Vulkan得靠 MoltenVK 把 Vulkan 转成 Metal多一层转换但兼容性往往更好。两套方案可以共存通过WINEDLLOVERRIDES切换用哪套 dll。5.3 关于 iOS 和 iPadOS 的延伸热搜里出现了不少 iOS 相关的词比如“ios 游戏”“ios 开发者模式”“ios 自动化”。严格来说Madeira 这套 x86-64 翻译方案在 iOS 上跑起来难度极大因为 iOS 对 JIT 有严格限制没有 JIT 权限动态翻译基本没法做而且沙盒限制多。所以目前这套方案的主战场还是 macOS。如果你看到有人宣称在 iOS 上跑 Windows 游戏大概率是串流或者远程桌面不是本地翻译。这点要拎清楚别被带偏。至于“ios 浏览器唤起安装 app”“notification banner 仿 ios 通知横幅”这些词跟 Madeira 的核心关系不大更多是移动端开发的周边话题。如果你是在做移动端开发这些可以作为独立方向去研究但别和跨架构兼容混为一谈。6. 我踩过的坑和几条实在建议最后说几条实打实的经验都是我在类似方案里真金白银换来的。第一条别追求“一次配好”。这套东西涉及三层任何一层版本变动都可能打破平衡。我的做法是配好一套能用的组合后把 FEX、Wine、DXMT 的版本号记下来甚至把整个 prefix 打包备份。下次要升级先备份再升出问题能秒回滚。第二条日志和最小复现是排查的两把刀。遇到问题别瞎试先开对应通道的日志看报错在哪一层。是 FEX 翻译失败还是 Wine API 没实现还是 DXMT 着色器编译不过日志里通常写得明明白白。定位到层再针对性地换版本或调配置。第三条性能预期要放平。两层翻译加图形转换性能损耗是客观存在的。老游戏、2D 游戏、对帧率不敏感的软件体验通常不错3A 大作、对延迟敏感的竞技游戏别抱太高期望。这套方案的价值在于“能跑”而不是“跑得跟原生一样快”。第四条社区是你最好的后盾。FEX、Wine、DXMT 都有活跃的社区遇到怪问题先搜 issue大概率有人踩过。提问时把版本、日志、复现步骤给全别只丢一句“跑不起来”没人能帮你。这套 Madeira 方案说到底是在苹果 ARM 生态和 Windows x86 生态之间架桥。桥能走但走起来颠簸。你要是享受折腾的过程愿意为“在 Mac 上跑 Windows 程序”这件事投入时间那它值得玩你要是只想开箱即用那虚拟机或者远程方案可能更省心。选择权在你但至少现在你知道了这座桥是怎么搭起来的。