1. 从“Madeira”说起:一个跨平台兼容层的真实需求
第一次看到“Madeira”这个标题,加上后面跟着的 Wine、FEX-Emu、DXMT、iOS、x86-64 这一串关键词,我脑子里第一反应是:这又是一个在“让不同架构、不同系统跑同一套东西”这件事上死磕的项目。做过跨平台开发或者折腾过兼容层的人都知道,这类项目从来不是单纯的技术炫技,而是被真实需求逼出来的——你手上有一堆为 x86-64 编译的 Windows 程序,但你的设备可能是 ARM 架构的 Mac,可能是 Linux 桌面,甚至可能是移动端环境,你不想重写,也不想开虚拟机,那怎么办?兼容层就是答案。
Madeira 这个名字本身是葡萄牙的一个岛屿,盛产葡萄酒,所以热搜词里出现“Wine”一点都不意外,这明显是个双关。但真正让我感兴趣的是它把 FEX-Emu 和 DXMT 放在了一起。FEX-Emu 是做 x86-64 到 ARM64 指令翻译的,DXMT 是把 Direct3D 转到 Metal 的,这两个东西组合起来,意味着 Madeira 想解决的是“在 ARM 设备上跑 x86-64 的 Windows 游戏或应用,并且把图形 API 也翻译过去”这一整条链路。这不是小打小闹,这是把 Wine 生态里最难的几个环节串起来了。
我之所以愿意花时间拆这个项目,是因为过去几年我在不同设备上折腾 Wine 的经历实在太多了。从最早的 deepin-wine 到后来麒麟系统的 Wine 助手,从 macOS 上的 CrossOver 到 Linux 上手动编译 Wine,踩过的坑能写一本书。而 Madeira 这个方向,恰好是我认为未来两三年内最有实用价值的兼容层方案之一。它适合谁看?如果你是那种需要在非 x86 设备上跑 Windows 程序的人,或者你是做跨平台工具链的开发者,再或者你只是对“为什么一个程序换个 CPU 就跑不起来”这件事好奇,那这篇内容应该能给你一些实在的东西。
2. 核心架构拆解:Madeira 到底在做什么
2.1 三层翻译链路的设计逻辑
要理解 Madeira,得先把它拆成三层来看。最底层是 CPU 指令层的翻译,这里用的是 FEX-Emu。FEX-Emu 做的事情简单说就是:ARM64 的 CPU 不认识 x86-64 的指令,那就在中间加一个翻译层,把 x86-64 的指令动态翻译成 ARM64 能执行的指令。这个过程是实时的,不是提前编译好的,所以对用户来说,你拿到的还是一个 x86-64 的二进制文件,但它在 ARM 设备上能跑起来。
中间层是系统调用和 API 的翻译,这是 Wine 的活。Wine 把 Windows 的系统调用翻译成 POSIX 兼容的调用,把 Windows 的 DLL 替换成自己实现的版本。这一层的关键在于“兼容性覆盖度”,也就是你到底实现了多少 Windows API。Wine 项目做了几十年,覆盖度已经相当高了,但总有一些边角案例会出问题,尤其是涉及到图形和音频的部分。
最上层是图形 API 的翻译,这里 Madeira 选择了 DXMT。DXMT 是把 Direct3D 翻译成 Metal 的项目,主要面向 Apple 平台。为什么不用 DXVK 或者 VKD3D?因为 DXVK 是把 D3D 转到 Vulkan,而 Metal 在 Apple 设备上是更原生的选择,性能损耗更小。DXMT 相当于在 Wine 和 Metal 之间架了一座桥,让 Windows 游戏能直接调用 Metal 的图形能力。
这三层叠起来,整条链路就是:x86-64 指令 → FEX-Emu → ARM64 指令 → Wine → POSIX 调用 → DXMT → Metal → GPU。每一层都有性能损耗,但每一层也都在做必要的翻译工作。Madeira 的价值在于它把这三层整合到了一个项目里,而不是让用户自己去拼。
2.2 为什么是 FEX-Emu 而不是 QEMU
很多人会问,既然要做指令翻译,为什么不用 QEMU?QEMU 也能做 x86-64 到 ARM64 的翻译,而且更成熟。这里的关键在于“用户态”和“系统态”的区别。QEMU 的全系统模拟是模拟整个硬件环境,包括 CPU、内存、外设,然后在这个模拟环境里跑一个完整的操作系统。这种方式兼容性最好,但性能损耗极大,因为每一条指令都要经过完整的模拟流程。
FEX-Emu 是用户态的翻译,它不需要模拟整个系统,只需要在现有系统上把 x86-64 的用户态程序翻译成 ARM64 能执行的代码。这意味着它可以直接利用宿主系统的内核、文件系统、网络栈,不需要额外模拟。性能上,FEX-Emu 比 QEMU 全系统模拟快一个数量级,尤其是在 CPU 密集型任务上。代价是它只能跑用户态程序,不能跑需要内核模块或者驱动的东西。
Madeira 选择 FEX-Emu,说明它的目标场景是“跑应用和游戏”,而不是“跑整个 Windows 系统”。这个定位很务实,因为大多数人的需求就是跑某个特定的程序,而不是要一个完整的 Windows 环境。
2.3 DXMT 在图形翻译中的位置
图形翻译这块,DXMT 的选择也很有讲究。Direct3D 到 Metal 的翻译,和 Direct3D 到 Vulkan 的翻译,在实现难度和性能表现上差别很大。Metal 是 Apple 自己的一套图形 API,底层直接对接 Apple 的 GPU 驱动,所以在 Apple 设备上,Metal 的调用开销比 Vulkan 小。但 Metal 的 API 设计和 D3D 差异较大,翻译层需要做很多状态映射和资源转换的工作。
DXMT 目前主要覆盖 D3D11 和部分 D3D12 的功能,对于大多数老游戏和部分新游戏来说够用了。但如果你要跑的是那种重度依赖 D3D12 新特性的游戏,可能还是会遇到问题。这是所有图形翻译层都面临的挑战:D3D 的版本迭代太快,翻译层永远在追赶。
Madeira 把 DXMT 集成进来,意味着它在 Apple 设备上的图形性能会比用 DXVK 转 Vulkan 再转 Metal 的方案好不少。少一层转换,就少一层开销。这也是为什么我一直在关注这个项目的原因之一。
3. 实操环境搭建:从零开始跑通 Madeira
3.1 基础依赖的安装与版本选择
假设你手头是一台 Apple Silicon 的 Mac,想跑一个 x86-64 的 Windows 程序。第一步是装基础依赖。你需要的东西包括:Homebrew(包管理器)、Xcode Command Line Tools(编译工具链)、以及 Madeira 本身。Homebrew 的安装很简单,一条命令的事,但要注意如果你的网络环境访问默认源比较慢,可以换国内镜像源,这个网上教程很多,我就不展开了。
Xcode Command Line Tools 用xcode-select --install就能装。这里有个坑:如果你之前装过完整版 Xcode,可能会遇到路径冲突的问题。解决办法是用xcode-select -p确认当前指向的路径,如果是/Applications/Xcode.app/Contents/Developer而不是/Library/Developer/CommandLineTools,需要用sudo xcode-select -s切换过来。这个细节很多人会忽略,然后编译的时候报一堆找不到头文件的错误。
Madeira 本身的安装,目前主要是从源码编译。你需要先 clone 仓库,然后按照 README 里的步骤来。这里我建议你先看一下项目的 release 页面,如果有预编译的二进制包,优先用二进制包,能省掉很多编译时间。源码编译的话,依赖项比较多,尤其是 FEX-Emu 和 DXMT 这两个子模块,编译一次可能要半小时以上。
3.2 Wine 前缀的初始化与配置
Madeira 跑起来之后,你需要初始化一个 Wine 前缀。Wine 前缀可以理解为一个“虚拟的 Windows 环境”,里面有自己的注册表、文件系统结构、DLL 库。每个前缀是独立的,你可以为不同的程序创建不同的前缀,避免互相干扰。
初始化命令大概是这样的:
madeira wineboot --init这个命令会创建一个默认的前缀,通常在~/.madeira/prefix或者类似的位置。初始化过程中,Wine 会弹出一些提示,比如让你安装 Mono 和 Gecko。Mono 是 .NET 的替代实现,Gecko 是浏览器引擎的替代实现。这两个东西对于跑某些程序是必需的,比如用 .NET 写的应用需要 Mono,内嵌网页的应用需要 Gecko。
这里有个经验:如果你只是跑一个简单的游戏,不需要 .NET 和网页功能,可以跳过这两个安装,能省不少时间和空间。但如果你不确定,还是装上,免得后面遇到问题再回来补。
初始化完成后,你可以用madeira winecfg来打开配置界面,调整 Windows 版本、显示设置、音频设置等。Windows 版本的选择很关键,有些程序在 Windows 7 模式下跑得好,有些需要 Windows 10 模式。这个没有统一答案,得根据具体程序来试。
3.3 图形后端的切换与调试
Madeira 默认应该会用 DXMT 作为图形后端,但你可以通过环境变量来切换。比如:
export MADEIRA_GRAPHICS_BACKEND=dxmt或者如果你想试试 DXVK 的方案:
export MADEIRA_GRAPHICS_BACKEND=dxvk切换后端之后,记得清理一下 Wine 前缀里的着色器缓存,否则可能会因为缓存不匹配导致渲染错误。缓存通常在~/.madeira/prefix/drive_c/users/你的用户名/Temp或者类似的目录下,删掉里面的*.dxcache和*.vkcache文件就行。
调试图形问题的时候,可以打开 DXMT 的日志输出:
export DXMT_LOG_LEVEL=debug这样运行程序的时候,终端会打印出详细的图形调用信息,方便定位问题。但要注意,日志量可能非常大,建议只在排查问题时开启,平时关掉。
4. 常见问题与排查技巧实录
4.1 Wine 乱码问题的根源与解决
“wine 乱码”是热搜词里出现频率很高的一个,说明这是很多人的痛点。乱码通常出现在两个地方:一是程序界面上的文字显示为方块或者问号,二是终端输出里的中文变成乱码。
界面乱码的原因一般是字体缺失。Wine 默认不带中文字体,你需要把系统的中文字体链接到 Wine 的字体目录里。在 macOS 上,系统字体在/System/Library/Fonts和/Library/Fonts下,你可以把需要的字体复制或者软链接到 Wine 前缀的drive_c/windows/Fonts目录。常用的中文字体包括 PingFang、Heiti、Songti 等。
终端乱码的原因通常是 locale 设置不对。你可以在运行 Wine 之前设置:
export LANG=zh_CN.UTF-8 export LC_ALL=zh_CN.UTF-8如果还是乱码,检查一下终端本身的编码设置,确保是 UTF-8。
还有一个容易被忽略的点:某些程序内部用的是 GBK 编码,而 Wine 默认按 UTF-8 处理,就会乱码。这种情况需要在 Wine 的注册表里调整代码页设置,或者用winecfg里的“区域设置”来改。
4.2 程序启动失败的分层排查法
程序启动失败是最常见的问题,排查的时候建议按层次来,从下往上查。
第一层:FEX-Emu 是否正常工作。你可以先跑一个简单的 x86-64 命令行程序,比如file命令的 x86-64 版本,看看能不能正常输出。如果这一步就失败,说明 FEX-Emu 的配置有问题,可能是环境变量没设对,或者二进制文件不兼容。
第二层:Wine 是否正常初始化。用madeira wineboot重新初始化一个干净的前缀,然后跑wine notepad看看能不能打开记事本。如果记事本能打开,说明 Wine 本身没问题。
第三层:程序的依赖是否满足。很多 Windows 程序依赖特定的运行库,比如 Visual C++ Redistributable、.NET Framework、DirectX 运行时。你可以用winetricks来安装这些依赖:
madeira winetricks vcrun2019 dotnet48winetricks 是个脚本集合,能自动下载和安装常见的 Windows 运行库。但要注意,有些运行库的安装过程本身就需要图形界面,如果你的图形后端没配好,可能会卡住。
第四层:图形 API 是否兼容。如果程序能启动但黑屏或者崩溃,大概率是图形翻译层的问题。这时候可以试试切换后端,或者降低游戏的图形设置。
4.3 性能调优的几个关键参数
性能是兼容层永远绕不开的话题。Madeira 的性能调优,我总结下来有几个关键点。
首先是 FEX-Emu 的 TSO 模式。TSO 是 Total Store Ordering 的缩写,x86 的内存模型比 ARM 更严格,FEX-Emu 需要额外的工作来保证内存顺序。开启 TSO 模式能提高兼容性,但会损失一些性能。你可以在 FEX-Emu 的配置里调整这个选项,具体命令取决于你的安装方式。
其次是 Wine 的 DLL 覆盖设置。某些 DLL 用 Wine 自带的实现比用原版 Windows DLL 性能更好,但兼容性可能差一些。你可以在winecfg的“函数库”标签页里,为特定的 DLL 设置“原生”或“内建”。比如d3d11.dll通常用内建的(也就是 DXMT 的实现),而msvcrt.dll可能用原生的更好。
最后是 DXMT 的着色器编译模式。DXMT 支持异步着色器编译,能减少游戏加载时的卡顿,但可能会导致某些着色器编译错误。你可以在 DXMT 的配置里开启或关闭这个选项,根据具体游戏来定。
| 调优项 | 推荐设置 | 适用场景 | 注意事项 |
|---|---|---|---|
| FEX-Emu TSO | 开启 | 兼容性优先 | 性能损失约 10-20% |
| DLL 覆盖 | d3d11=n,b | 大多数游戏 | 个别游戏需要原生 d3d11 |
| 异步着色器 | 开启 | 减少卡顿 | 可能出现着色器错误 |
| Wine 版本 | Windows 10 | 新游戏 | 老游戏试试 Windows 7 |
4.4 那些文档里不会写的坑
第一个坑:macOS 的 SIP(系统完整性保护)会阻止某些操作。如果你在编译或者运行过程中遇到“Operation not permitted”的错误,可能需要临时关闭 SIP。但我要提醒一句,关闭 SIP 有安全风险,做完事情后记得重新打开。
第二个坑:Apple Silicon 的 Rosetta 2 和 FEX-Emu 可能会冲突。Rosetta 2 是 Apple 自己的 x86-64 翻译层,如果你在 ARM Mac 上跑 x86-64 的 Madeira,系统可能会优先用 Rosetta 2 来翻译 Madeira 本身,而不是让 Madeira 用 FEX-Emu 去翻译 Windows 程序。这会导致性能异常。解决办法是确保 Madeira 的二进制是 ARM64 原生的,或者在启动时用arch -arm64强制指定架构。
第三个坑:Wine 前缀的路径里不要有中文或者空格。这个问题在 Windows 上很常见,在 Wine 里也一样。路径里有中文或者空格,可能会导致某些程序找不到文件。建议把前缀放在纯英文、无空格的路径下。
第四个坑:某些反作弊系统会检测到 Wine 环境并拒绝运行。这是没办法的事,只能找那些不依赖反作弊的游戏来玩。
5. 从 Madeira 看兼容层的未来走向
5.1 移动端 iOS 的可能性与限制
热搜词里出现了 iOS、ios 游戏、ios 开发者模式这些,说明很多人关心 Madeira 能不能在 iOS 上跑。从技术上讲,iOS 的内核是 Darwin,和 macOS 同源,Wine 在 iOS 上理论上是可行的。但实际限制很多:iOS 不允许 JIT(即时编译),而 FEX-Emu 和 Wine 都依赖 JIT 来翻译指令。没有 JIT,性能会差到没法用。另外 iOS 的沙盒机制也限制了 Wine 访问文件系统的能力。
所以短期内,Madeira 在 iOS 上跑 Windows 程序的可能性不大。但如果是越狱设备,或者通过开发者模式侧载,可能会有一些实验性的方案。这个方向我持谨慎乐观态度,但不会建议普通用户去尝试。
5.2 与国产系统 Wine 方案的对比
热搜词里还有“麒麟 wine 助手”、“统信 wine windows 兼容组件下载”这些,说明国产 Linux 发行版也在做类似的事情。麒麟和统信的 Wine 方案,主要是把 Wine 和 winetricks 打包成图形化的助手,降低用户的使用门槛。这个思路和 Madeira 不太一样:Madeira 更偏向技术整合和性能优化,而国产系统的方案更偏向易用性和本地化适配。
从技术栈上看,国产系统的 Wine 方案大多还是基于 x86 架构,因为国产 CPU 里 x86 的占比不小。而 Madeira 面向的是 ARM 设备,解决的是架构翻译的问题。两者有交集,但侧重点不同。如果你用的是飞腾或者鲲鹏的 ARM 服务器,Madeira 的思路可能更有参考价值。
5.3 开发者可以从中借鉴什么
如果你是个开发者,Madeira 这个项目有几个地方值得学习。
一是分层设计的思路。把指令翻译、系统调用翻译、图形 API 翻译分成独立的层,每层可以单独替换和升级。这种设计让项目更灵活,也更容易维护。
二是对性能瓶颈的取舍。FEX-Emu 选择用户态翻译而不是全系统模拟,DXMT 选择 Metal 而不是 Vulkan,都是在性能和兼容性之间做的权衡。做技术方案的时候,明确自己的目标场景,然后针对性地优化,比追求“什么都能跑”更实际。
三是对社区生态的利用。Madeira 没有重新造轮子,而是把 Wine、FEX-Emu、DXMT 这些成熟项目整合起来。这种“站在巨人肩膀上”的做法,对于资源有限的团队来说,是更聪明的选择。
6. 一些实操后的个人体会
折腾 Madeira 这段时间,我最大的感受是:兼容层这东西,永远没有“完美”的时候。你今天跑通了一个程序,明天可能就因为系统更新或者程序更新而挂掉。所以心态要放平,把它当成一个“能跑就赚到”的工具,而不是“必须能跑”的依赖。
另外,社区的力量真的很重要。Wine 的 AppDB 里有大量用户提交的兼容性报告,Madeira 的 issue 区也有很多人分享配置经验。遇到问题的时候,先搜一下有没有人遇到过类似的,往往能省很多时间。
最后分享一个小技巧:如果你要跑的程序比较老,试试在 Wine 配置里把 Windows 版本设成 Windows XP 或者 Windows 7,同时关闭一些新的图形特性。老程序往往对新环境更挑剔,降级反而能跑得更稳。这个经验我在跑一些 2000 年代的游戏时屡试不爽。