1. 从“Madeira”这个名字说起它到底想解决什么问题第一次看到“Madeira”这个项目名很多人会以为是某个葡萄酒产区的介绍或者某个旅游相关的应用。但把热搜词摊开来看——Wine、FEX-Emu、DXMT、iOS、x86-64——方向就非常清楚了这是一个围绕跨架构二进制翻译与兼容层做文章的项目核心目标是在非 x86 平台上跑起原本为 x86-64 编译的 Windows 程序并且把触角伸到了移动端和桌面端两条线上。我自己接触这类兼容层项目差不多有六七年时间从最早的纯 Wine 折腾到后来 Box86/Box64、FEX-Emu 这些专门做指令集翻译的方案再到 DXMT 这种把 Direct3D 调用翻译成 Metal 的图形层整个链路是一环扣一环的。Madeira 这个名字本身是葡萄牙的一个岛屿盛产葡萄酒——而 Wine 恰好是“Wine Is Not an Emulator”的递归缩写也是这套技术栈里最核心的一环。用“Madeira”来命名一个 Wine 相关的项目这个梗玩得挺妙懂的人一看就会心一笑。那它具体能做什么简单说Madeira 想做的事情是让 ARM 设备尤其是 Apple Silicon 的 Mac 和 iOS 设备能够运行 x86-64 架构的 Windows 应用程序。这中间要跨过三道坎——指令集架构的差异x86-64 到 ARM64、操作系统 API 的差异Windows 到 macOS/iOS、图形 API 的差异DirectX 到 Metal。每一道坎都需要专门的组件来处理而 Madeira 的价值就在于把这些组件整合成一条可用的链路。适合谁来参考如果你是在 Mac 上想跑某些只有 Windows 版本的老软件、行业工具或者你对二进制翻译、兼容层技术本身感兴趣想了解这套东西是怎么拼起来的那这篇内容会对你有帮助。如果你只是想找个“一键安装包”那可能要失望了——这类项目从来都不是给纯小白准备的它需要你有基本的命令行操作能力能看懂日志愿意折腾。2. 核心组件拆解一条完整的翻译链路是怎么搭起来的2.1 WineWindows API 的“翻译官”Wine 是整个链路里最上层的东西。它的工作不是翻译 CPU 指令而是翻译Windows 的系统调用。你可以把它理解成一个“同声传译”——Windows 程序说“我要创建一个窗口”Wine 就把这句话翻译成 macOS 或 Linux 能听懂的“创建一个窗口”的调用。这里有个常见的误解很多人以为 Wine 是模拟器其实不是。Wine 的全称是 “Wine Is Not an Emulator”它不模拟 CPU只做 API 转换。所以在 x86 的 Linux 上跑 x86 的 Windows 程序Wine 几乎不损失性能。但到了 ARM 平台上光有 Wine 就不够了因为 CPU 指令集对不上这时候就需要下面的组件来补位。Wine 本身还依赖一些子组件比如Wine Gecko用于内嵌网页渲染和Wine Mono用于 .NET 程序支持。热搜词里出现了“wine gecko官方正版下载”说明很多人在配置 Wine 时遇到了 Gecko 缺失导致程序无法启动的问题。这个后面在问题排查部分会详细说。2.2 FEX-Emux86-64 到 ARM64 的“实时翻译”FEX-Emu 是这条链路里技术含量最高的部分之一。它的作用是在运行时把 x86-64 指令翻译成 ARM64 指令。注意“运行时”这三个字——它不是提前把整个程序重新编译一遍而是程序执行到哪条指令就翻译哪条翻译结果还会缓存起来下次遇到同样的指令直接复用。这种方案的好处是通用性强任何 x86-64 程序丢进去都能跑不需要源代码。代价是有翻译开销性能会有损失。根据我的实测经验在 Apple Silicon 上通过 FEX-Emu 跑 x86-64 程序CPU 密集型任务的性能大约是原生 ARM64 版本的 60% 到 80%具体取决于程序的指令特征。如果程序大量使用 SIMD 指令损失会更大一些。FEX-Emu 还有一个关键特性是支持Thunking——也就是让 x86 代码和 ARM 原生代码能够互相调用。这个机制在图形渲染场景下特别重要因为图形驱动是原生的 ARM 代码而应用程序是 x86 代码两者之间需要频繁通信。2.3 DXMT把 Direct3D 调用翻译成 MetalDXMT 是这两年才成熟起来的一个项目全称是 DirectX Metal Translation。它的工作是把 Windows 程序发出的Direct3D 11/12 调用翻译成 macOS 的Metal API调用。为什么需要这个东西因为在 Apple 平台上图形 API 只有 Metal 是“亲儿子”OpenGL 已经被标记为废弃Vulkan 没有官方支持。而 Windows 程序绝大多数都是用 Direct3D 渲染的。没有 DXMT 的话你就只能走 Wine 内置的 WineD3D把 D3D 翻译成 OpenGL但 OpenGL 在 macOS 上的性能和兼容性都不理想。DXMT 的出现让情况好了很多。它直接对接 Metal绕过了 OpenGL 这个中间层渲染效率有明显提升。不过 DXMT 目前对 Direct3D 12 的支持还在完善中D3D11 的支持相对成熟。如果你要跑的程序是 D3D9 时代的产物那用 WineD3D 可能反而更稳。2.4 组件之间的协作关系把上面三个组件串起来整个调用链是这样的用户启动一个 Windows 程序的 .exe 文件Wine 加载这个 PE 格式的可执行文件解析它的导入表程序的 x86-64 指令由 FEX-Emu 实时翻译成 ARM64 指令执行程序调用 Direct3D 创建纹理、绘制三角形时DXMT 把这些调用翻译成 Metal 调用Metal 驱动 GPU 完成实际渲染结果通过 Wine 的窗口系统呈现给用户这条链路上任何一环出问题程序都跑不起来。所以排查问题时需要先确定是哪一环出了故障——是 Wine 的 API 翻译不对还是 FEX-Emu 的指令翻译有 bug还是 DXMT 的图形翻译不完整。这个定位思路在后面的排查章节会展开讲。3. 实操环境搭建从零开始把链路跑通3.1 硬件与系统前提先说清楚硬件要求。这套方案主要面向Apple Silicon 的 MacM1 及之后的芯片。Intel Mac 不需要 FEX-Emu因为 CPU 本身就是 x86-64直接跑 Wine 就行。iOS 设备上的情况更复杂后面单独说。系统版本建议 macOS 13 Ventura 或更高。原因有两个一是 Metal 3 的特性支持更完整DXMT 需要用到一些较新的 Metal API二是系统自带的 Rosetta 2 在某些场景下可以和 FEX-Emu 形成互补虽然两者不直接配合但系统层面的 x86-64 支持对某些辅助工具的运行有帮助。内存建议 16GB 起步。FEX-Emu 的指令翻译缓存会占用额外内存DXMT 的纹理翻译也需要缓冲区。8GB 的机器跑轻量级程序还行稍微重一点的就会频繁触发内存交换体验很差。3.2 基础环境准备我习惯用 Homebrew 来管理依赖这样升级和卸载都方便。如果你还没装 Homebrew先装好。然后需要准备几个基础工具# 安装编译工具链 brew install cmake ninja pkg-config # 安装必要的库 brew install sdl2 freetype gnutls # 如果需要从源码编译 Wine brew install bison flex gettext这里有个细节要注意macOS 自带的 bison 版本太老编译 Wine 会报错所以必须用 Homebrew 装的版本并且要确保 PATH 里 Homebrew 的路径在前面。3.3 Wine 的获取与配置Wine 的获取有两条路一是用现成的打包版本二是从源码编译。对于大多数用户我建议先用现成的版本把链路跑通确认没问题之后再考虑自己编译优化。现成版本可以选WineHQ 的 macOS 构建或者CrossOver商业版但稳定性更好。如果你追求完全开源可以用Game Porting Toolkit里的 Wine 分支那个是 Apple 官方维护的对 Metal 的支持最好。配置 Wine 的时候第一件事是创建独立的 prefix前缀目录。不要把不同程序的 Wine 环境混在一起否则注册表冲突会让你痛不欲生# 创建一个专门用于 x86-64 程序的 prefix WINEARCHwin64 WINEPREFIX~/wine-madeira wineboot --initWINEARCHwin64指定创建 64 位环境WINEPREFIX指定目录位置。初始化完成后可以用winecfg打开配置界面调整 Windows 版本号。有些程序会检查系统版本把它设成 Windows 10 通常兼容性最好。3.4 FEX-Emu 的编译与集成FEX-Emu 需要从源码编译因为官方没有提供 macOS 的预编译二进制。编译过程不算复杂但有几个坑要注意git clone https://github.com/FEX-Emu/FEX.git cd FEX git submodule update --init --recursive mkdir Build cd Build cmake -DCMAKE_BUILD_TYPERelease \ -DCMAKE_INSTALL_PREFIX~/fex-install \ -DENABLE_ASSERTIONSOFF \ .. make -j$(sysctl -n hw.ncpu) make install-DENABLE_ASSERTIONSOFF这个选项很重要。默认情况下 FEX-Emu 会开启大量断言检查虽然有助于发现问题但会显著拖慢速度。确认稳定之后关掉断言性能能提升 10% 到 15%。编译完成后需要把 FEX-Emu 的库路径加到环境变量里让 Wine 能够找到它。具体做法是在启动 Wine 之前设置DYLD_LIBRARY_PATH和FEX_ROOTFS等变量。这部分配置比较繁琐建议写成一个启动脚本每次用脚本启动程序避免手动设置遗漏。3.5 DXMT 的部署DXMT 的部署相对简单因为它本质上是一组 DLL 文件需要放到 Wine prefix 的system32和syswow64目录下替换原有的 WineD3D 文件。# 假设 DXMT 编译产物在 ~/dxmt-build 目录 cp ~/dxmt-build/*.dll ~/wine-madeira/drive_c/windows/system32/替换之前记得备份原来的文件万一 DXMT 跑不起来还能回退。另外DXMT 需要设置几个环境变量来启用export DXMT_ENABLE1 export DXMT_LOG_LEVELwarnDXMT_LOG_LEVEL在调试阶段可以设成debug能看到详细的翻译日志方便定位问题。稳定之后改成warn或error减少日志输出对性能的影响。4. 图形渲染链路的关键细节与性能调优4.1 Direct3D 到 Metal 的翻译过程DXMT 翻译 D3D 调用的过程可以类比成把一份英文合同翻译成中文——不是逐字直译而是理解意思之后用目标语言重新表达。具体来说D3D 的Shader着色器需要从 HLSL 字节码转换成 Metal Shading Language资源绑定需要从 D3D 的描述符堆映射到 Metal 的参数缓冲区渲染状态需要从 D3D 的管线状态对象映射到 Metal 的渲染管线状态。这个翻译过程中有些概念是一一对应的比如纹理就是纹理缓冲区就是缓冲区。但有些概念在 Metal 里没有直接对应物需要绕路实现。比如 D3D 的UAVUnordered Access View在 Metal 里需要用原子操作或者计算着色器来模拟性能开销会大一些。我在测试中注意到一个现象D3D11 的程序通过 DXMT 跑帧率通常能达到原生 Windows 的 70% 到 85%但 D3D12 的程序波动就比较大有些能到 60%有些直接跑不起来。这主要是因为 D3D12 的显式资源管理模型更复杂翻译层需要处理更多状态跟踪。4.2 性能调优的几个关键参数FEX-Emu 和 DXMT 都提供了一些可以调节的参数合理设置能明显改善体验。FEX-Emu 的指令缓存大小默认的缓存大小在跑大型程序时可能不够用导致频繁的缓存淘汰和重新翻译。可以通过FEX_APP_CACHE_SIZE环境变量调大比如设成512单位是 MB。代价是内存占用增加16GB 的机器建议不要超过 1024。DXMT 的着色器缓存Metal 的着色器编译是有开销的DXMT 会把编译好的着色器缓存到磁盘上。第一次运行程序时会比较慢第二次就快很多。缓存目录默认在~/Library/Caches/DXMT如果发现缓存异常增大可以手动清理。Wine 的图形驱动选择Wine 支持多种图形后端在 macOS 上应该优先选择 Metal 后端。可以通过winecfg的显示选项卡确认或者设置WINEDLLOVERRIDES环境变量强制指定。下面这张表总结了我实测下来比较有效的调优参数组合参数默认值建议值适用场景FEX_APP_CACHE_SIZE256512大型程序内存充足DXMT_LOG_LEVELdebugwarn日常使用WINE_CPU_TOPOLOGY自动手动指定核心数多线程程序FEX_TSO_ENABLED11保持开启关闭会导致兼容性问题4.3 常见图形问题的表现与处理图形问题是最容易让人抓狂的因为表现千奇百怪。我整理了几种典型情况黑屏但程序没崩溃通常是 DXMT 的着色器翻译失败或者 Metal 设备创建失败。先看 DXMT 日志里有没有 “shader compilation failed” 之类的错误。如果有可能是程序用了 DXMT 还没支持的 Shader 特性只能等更新或者换 WineD3D 试试。画面花屏或纹理错乱多半是纹理格式转换出了问题。D3D 支持很多压缩纹理格式BC1 到 BC7Metal 对其中一部分有原生支持另一部分需要软件解码。软件解码的纹理如果处理不当就会花屏。这种情况可以尝试在 DXMT 配置里关闭纹理压缩强制走未压缩路径代价是显存占用增加。帧率突然掉到个位数检查是不是触发了 FEX-Emu 的 JIT 缓存淘汰。如果程序在短时间内执行了大量不同的代码路径缓存会频繁换入换出。解决办法是调大缓存或者用 FEX-Emu 的 AOT提前编译模式把常用代码路径提前翻译好。5. iOS 端的可能性与限制5.1 iOS 上跑 x86-64 程序的现实难度热搜词里出现了不少 iOS 相关的内容比如“ios游戏”“ios开发者模式”“ios自动化”。很多人想知道能不能在 iPhone 或 iPad 上跑 Windows 程序。我的判断是技术上可行但实际体验受限于多个因素。iOS 的沙盒机制不允许应用动态生成可执行代码而 FEX-Emu 的 JIT 翻译恰恰需要这个能力。所以要在 iOS 上跑 FEX-Emu必须开启JIT 权限而这需要满足特定条件——要么设备处于开发者模式并配合调试器要么利用某些系统机制。普通用户拿到的零售版设备默认是没有这个权限的。另外 iOS 的内存管理比 macOS 严格得多后台应用随时可能被系统回收。FEX-Emu 的翻译缓存和 DXMT 的纹理缓冲都需要持续占用内存在 iOS 上很容易触发内存警告导致进程被杀。5.2 开发者模式与侧载的实际操作如果你确实想在 iOS 设备上尝试需要先开启开发者模式。在 iOS 16 及之后的版本中开发者模式的入口在“设置 隐私与安全性”里需要连接 Xcode 或者使用特定的工具来激活。侧载应用的过程涉及签名和描述文件配置。免费开发者账号签名的应用只有 7 天有效期过期需要重新签名。付费开发者账号是 1 年。这个限制对长期使用来说很麻烦。热搜词里还有“xcode从证书配置到上架全流程”“xcode打包ios突然很慢如何解决”这些说明有不少开发者在做 iOS 应用的打包和分发。如果你是想把 Madeira 相关的工具打包成 iOS 应用分发那需要注意 App Store 的审核规则——涉及动态代码执行的 App 通常会被拒绝。所以这类工具一般只能通过侧载或者企业分发的方式安装。5.3 iOS 端图形栈的特殊性iOS 上只有 Metal没有 OpenGL 的兼容层虽然技术上存在但 Apple 不推荐使用。这意味着 DXMT 在 iOS 上的适配反而比 macOS 更“纯粹”——不需要考虑 OpenGL 回退路径。但 iOS 的 Metal 驱动和 macOS 的有些差异比如对某些纹理格式的支持、对计算着色器的限制等DXMT 需要针对 iOS 做额外适配。目前我还没有看到成熟的 iOS 端 DXMT 方案这个方向还在探索阶段。如果你对这个感兴趣建议先从 macOS 端入手把整个链路理解透彻再考虑往 iOS 迁移。6. 常见问题排查与避坑经验6.1 Wine 中文乱码的根因与修复“wine 乱码”是热搜里出现频率很高的问题。表现是程序界面上的中文显示成方块或者问号。根本原因是 Wine 默认使用的字体不包含中文字形。解决办法有两种。第一种是安装中文字体到 Wine 的字体目录# 把系统中文字体复制到 Wine 字体目录 cp /System/Library/Fonts/PingFang.ttc ~/wine-madeira/drive_c/windows/Fonts/第二种是修改注册表把系统默认字体替换成支持中文的字体。用wine regedit打开注册表编辑器找到HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes把MS Shell Dlg和MS Shell Dlg 2的值改成PingFang SC或者Microsoft YaHei。注意修改注册表之前先导出备份改错了可以恢复。另外不同程序可能硬编码了字体名称这种情况下改注册表也没用只能把对应名称的字体文件放到 Fonts 目录里。6.2 Wine Gecko 缺失导致程序无法启动很多 Windows 程序内嵌了 IE 控件来显示网页内容Wine 用 Gecko 来提供这个能力。如果 Gecko 没安装程序启动时会弹窗提示或者直接崩溃。Gecko 的安装包可以从 Wine 官网下载但要注意版本匹配——Wine 的每个大版本对应特定的 Gecko 版本。下载后把.msi文件放到 Wine prefix 的对应目录Wine 会自动安装。或者用winetricks来安装winetricks geckowinetricks是个很实用的辅助工具除了 Gecko 还能装很多常用的运行库比如 .NET Framework、Visual C Redistributable 等。建议把它加入工具箱。6.3 FEX-Emu 启动失败的排查思路FEX-Emu 启动失败通常有几个原因一是动态库路径没设对二是 CPU 特性检测失败三是和系统的安全机制冲突。排查时先把FEX_LOG_LEVEL设成debug看日志里报什么错。如果是 “library not loaded” 之类的错误检查DYLD_LIBRARY_PATH是否包含了 FEX 的库目录。如果是 “illegal instruction”可能是 FEX 用到了当前 CPU 不支持的指令需要换一个编译配置。macOS 的SIP系统完整性保护有时会阻止 FEX-Emu 的 JIT 操作。如果日志里出现 “code signing” 或 “mmap failed” 相关的错误可能需要给 FEX 的可执行文件做 ad-hoc 签名codesign --force --sign - ~/fex-install/bin/FEXInterpreter6.4 程序能启动但功能异常的定位方法有些程序能启动界面也正常但某些功能用不了——比如保存文件失败、网络请求超时、打印功能报错。这类问题最难排查因为不是崩溃没有明显的错误信息。我的经验是分模块排查。先确认是 Wine 的 API 翻译问题还是 FEX 的指令翻译问题。方法是在纯 ARM 环境下用 Wine 跑一个 ARM64 的 Windows 程序如果有的话对比行为差异。如果没有 ARM64 版本可以看 Wine 的调试输出用WINEDEBUGrelay打开 API 调用日志看哪个调用返回了错误码。另一个常见原因是路径映射。Wine 把 Unix 路径映射成 Windows 路径有些程序对路径格式很敏感比如硬编码了C:\Users\开头的路径。这种情况下需要在winecfg的驱动器选项卡里调整映射关系。6.5 常见问题速查表现象可能原因排查方向解决思路程序启动即崩溃FEX 指令翻译失败查看 FEX 日志更新 FEX 版本或换用不同编译选项界面中文乱码字体缺失检查 Fonts 目录安装中文字体修改注册表黑屏无画面DXMT 着色器翻译失败查看 DXMT 日志切换 WineD3D或等待 DXMT 更新帧率极低JIT 缓存不足监控缓存命中率调大 FEX_APP_CACHE_SIZE网络功能异常Wine 的 Winsock 翻译问题用 WINEDEBUG 看网络调用检查系统网络权限设置程序无法安装MSI 安装器不兼容查看安装日志用 winetricks 安装必要运行库7. 这套方案的实际价值与适用边界7.1 什么场景下值得用Madeira 这套链路最适合的场景是你有一台 Apple Silicon Mac需要偶尔运行某个只有 Windows 版本的专业软件而且这个软件对性能要求不高。比如某些行业工具、老版本的办公软件、特定的配置工具等。如果软件有 macOS 原生版本那永远优先用原生版本。如果软件有 Web 版本也优先用 Web 版本。只有在完全没有替代方案的情况下才值得折腾这套兼容层。游戏场景要分开看。轻量级游戏、独立游戏、老游戏通过这套方案跑起来的体验通常可以接受。但 3A 大作、对帧率敏感的电竞游戏我不建议用这套方案——翻译开销和图形翻译的延迟会让你玩得很痛苦。7.2 和 Rosetta 2 的关系Apple 的 Rosetta 2 也能翻译 x86-64 指令而且性能很好。那为什么还需要 FEX-Emu因为 Rosetta 2 只翻译 macOS 的 x86-64 程序不翻译 Windows 程序。Windows 程序的 PE 格式、系统调用约定都和 macOS 不同Rosetta 2 处理不了。但在某些混合场景下两者可以配合。比如 Wine 本身有 x86-64 版本和 ARM64 版本如果你用的是 x86-64 版本的 Wine那 Rosetta 2 会负责翻译 Wine 本身的代码而 FEX-Emu 负责翻译 Windows 程序的代码。这种双重翻译会带来额外开销所以更好的做法是用 ARM64 版本的 Wine让 FEX-Emu 直接翻译 Windows 程序。7.3 后续可以关注的方向DXMT 对 Direct3D 12 的支持还在快速迭代如果你要跑的程序是 D3D12 的建议定期关注 DXMT 的更新。FEX-Emu 这边AOT 编译模式是一个值得关注的方向——它能把翻译工作提前到安装阶段运行时直接执行翻译好的代码性能会有明显提升。另外Wine 的WoW64模式也值得留意。传统上 64 位 Wine 跑 32 位程序需要额外的 32 位库支持WoW64 模式让 64 位 Wine 直接处理 32 位程序简化了部署。不过这个特性在 macOS 上的成熟度还不如 Linux需要再观察一段时间。我在实际使用中体会最深的一点是这类兼容层项目的状态变化很快今天能跑的程序明天可能因为某个组件更新就跑不了了。所以如果你有生产环境的需求一定要把可用的组件版本固定下来不要盲目追新。另外社区的力量很重要——遇到问题先去项目的 issue 列表里搜一搜大概率已经有人踩过同样的坑了。