1. 从“Madeira”这个名字说起:它到底指什么
第一次看到“Madeira”这个词,多数人脑子里蹦出来的是葡萄牙那个产葡萄酒的海岛。但在技术圈,尤其是折腾跨平台兼容层和模拟运行环境的那拨人眼里,Madeira 往往是一个项目代号、一个构建目标,或者干脆就是某个实验性分支的名字。我拿到这个标题的时候,正文是空的,关键词也是空的,只有一串热搜词摆在那里:FEX-Emu、Wine、DXMT、iOS、x86-64。这几个词凑在一起,指向性其实非常明确——这是一个关于在非 x86 架构上跑 x86-64 程序、并且涉及图形指令转换和移动端生态的兼容性项目。
先把这几个核心概念的关系理清楚,不然后面没法聊。FEX-Emu 是一个用户态的 x86-64 指令模拟器,它的定位和 QEMU 的用户态模式有点像,但更专注于游戏和图形密集型负载,尤其是在 ARM64 设备上跑 x86-64 的 Linux 程序。Wine 是大家熟悉的 Windows 兼容层,它把 Windows 的 API 调用翻译成 POSIX 调用,让 Windows 程序不用虚拟机就能在类 Unix 系统上跑。DXMT 则是把 Direct3D 调用翻译成 Metal 的中间层,主要服务于 Apple 平台。iOS 和 x86-64 放在一起,说明这个项目大概率涉及在移动设备或者 Apple 芯片设备上运行原本为 x86-64 编译的 Windows 程序。
Madeira 在这个语境下,我倾向于认为它是一个整合方案的名字——把 FEX-Emu 的指令翻译能力、Wine 的 API 兼容能力、DXMT 的图形翻译能力串起来,目标平台可能是 iOS 或者搭载 Apple Silicon 的设备。为什么这么判断?因为热搜词里同时出现了“ios 开发者模式”“ios 自动化”“xcode 打包 ios 突然很慢如何解决”这些开发运维向的词,也有“wine 乱码”“wine 栏是乱码”“麒麟 wine 助手”这些实际使用中的问题词。这说明关注这个项目的人,既有在移动端做开发和部署的,也有在桌面 Linux 上折腾 Wine 的,两拨人的需求在 Madeira 这个点上产生了交集。
提示:如果你只是想知道 Madeira 是不是某个具体软件的官方名称,那可能要失望了。从现有信息看,它更像是一个社区驱动的实验性整合项目,或者某个内部构建目标的代号。下面的内容基于这个判断展开,重点放在这类项目通用的技术路径和实操经验上。
2. FEX-Emu 在 Madeira 里扮演的角色:为什么不用 QEMU
2.1 用户态模拟和系统态模拟的分水岭
很多人一提到“在 ARM 上跑 x86 程序”,第一反应是 QEMU。QEMU 确实能干活,但它有两种模式:系统态模拟和用户态模拟。系统态模拟要模拟整个硬件环境,包括 CPU、内存控制器、外设,然后在这个模拟出来的机器上跑一个完整的操作系统。这种方式的优点是隔离性好,缺点是性能损耗大,尤其是图形和 I/O 密集的场景,帧率能掉到个位数。
FEX-Emu 走的是另一条路。它只模拟 CPU 指令集,把 x86-64 的指令动态翻译成 ARM64 指令,系统调用直接透传给宿主内核。这意味着它不需要模拟整个操作系统,只需要处理指令翻译和少量的 ABI 适配。对于 Madeira 这种要跑 Windows 程序的目标来说,FEX-Emu 负责的是最底层的指令翻译,上面跑的是 Wine 的 Windows API 实现,再上面才是具体的应用程序。
这个分层很关键。我见过不少人把 FEX-Emu 和 Wine 混为一谈,以为装了 Wine 就能在 ARM 上跑 x86 的 Windows 程序。实际上 Wine 本身不处理指令集差异,它假设你的 CPU 能直接执行目标程序的指令。在 x86 机器上,Wine 直接跑 x86 程序没问题;但在 ARM 机器上,Wine 需要 FEX-Emu 这样的指令翻译层垫在下面,才能把 x86 指令转成 ARM 能执行的东西。
2.2 FEX-Emu 的性能取舍和配置要点
FEX-Emu 的指令翻译是动态的,也就是说它在程序运行过程中实时把 x86 指令块翻译成 ARM64 指令块,然后缓存起来。第一次执行某段代码会慢,后续命中缓存就快很多。这个机制决定了它的性能表现和程序的执行模式强相关。如果一个程序频繁地跳转到新的代码路径,缓存命中率低,性能就会明显下降;反之,如果程序在一个相对固定的循环里跑,缓存命中率高,性能可以接近原生。
在 Madeira 这类项目里配置 FEX-Emu,有几个参数值得关注。首先是FEX_TSOEN,这个控制是否启用 x86 的内存序模型。x86 是强内存序,ARM 是弱内存序,启用 TSO 模拟能保证多线程程序的正确性,但会带来性能开销。如果目标程序是单线程的,或者对内存序不敏感,可以关掉它换性能。其次是FEX_ROOTFS,指定根文件系统的路径,Wine 的目录结构要放在这里面。还有FEX_MULTIBLOCK,控制是否启用多块编译,开启后能提高缓存效率,但会增加编译时间。
我自己的经验是,在 Apple Silicon 设备上跑 FEX-Emu,先把 TSO 打开保证兼容性,等程序能稳定运行了,再尝试关掉 TSO 看性能提升是否明显。如果程序本身没有多线程竞争的问题,关掉 TSO 通常能有 10% 到 20% 的性能提升。但如果是游戏或者数据库这类对内存序敏感的程序,关掉 TSO 可能会出各种奇怪的 bug,比如数据竞争导致的崩溃或者逻辑错误。
2.3 FEX-Emu 和 Wine 的对接细节
FEX-Emu 和 Wine 对接的时候,最容易出问题的地方是路径映射和库加载。Wine 有一套自己的目录结构,比如C:\windows\system32对应到宿主文件系统的某个目录。FEX-Emu 在翻译指令的时候,需要能正确解析这些路径,把 Windows 风格的路径转换成宿主系统的路径。如果路径映射配错了,程序会报“找不到 DLL”或者“无法加载库”之类的错误。
另一个坑是 32 位和 64 位的混合。FEX-Emu 主要处理 x86-64,但很多 Windows 程序是 32 位的,或者包含 32 位的组件。Wine 有 WoW64 机制来处理 32 位程序,但在 FEX-Emu 上面跑 WoW64 需要额外的配置。我试过在 ARM 设备上跑一个 32 位的 Windows 程序,Wine 的 WoW64 层和 FEX-Emu 的指令翻译层叠在一起,性能损耗非常明显,而且稳定性堪忧。如果目标程序有 64 位版本,尽量用 64 位版本,能省掉很多麻烦。
3. DXMT 的图形翻译链路:从 Direct3D 到 Metal
3.1 为什么不是 DXVK 或者 VKD3D
在 Linux 上跑 Windows 游戏,大家最熟悉的是 DXVK,它把 Direct3D 调用翻译成 Vulkan。但在 Apple 平台上,Vulkan 的支持并不好,Metal 才是原生的图形 API。DXMT 就是为这个场景设计的,它把 Direct3D 翻译成 Metal,让 Windows 游戏能在 macOS 和 iOS 上利用 Metal 的图形能力。
Madeira 如果涉及在 Apple 设备上跑 Windows 程序,DXMT 几乎是绕不开的一环。DXVK 在 Apple 平台上也能用,但需要 MoltenVK 把 Vulkan 转成 Metal,多了一层转换,性能和兼容性都会打折扣。DXMT 直接对接 Metal,少了一层中间层,理论上效率更高。不过 DXMT 的成熟度不如 DXVK,遇到冷门游戏或者特殊的 Direct3D 特性,可能会渲染错误或者直接崩溃。
3.2 DXMT 的配置和常见渲染问题
DXMT 的配置主要通过环境变量来控制。DXMT_D3D12控制是否启用 Direct3D 12 的支持,DXMT_METALFX控制是否启用 MetalFX 超分辨率,DXMT_SHADER_CACHE指定着色器缓存的路径。着色器缓存很重要,因为 Metal 的着色器编译比较慢,如果没有缓存,每次启动游戏都要重新编译一遍,加载时间会很长。
我遇到过的渲染问题里,最常见的是纹理错乱和画面闪烁。纹理错乱通常是格式转换的问题,Direct3D 的纹理格式和 Metal 的纹理格式不是一一对应的,DXMT 需要做转换,如果转换逻辑有 bug,纹理就会显示异常。画面闪烁则多半是同步问题,Direct3D 的呈现模型和 Metal 的呈现模型有差异,帧的同步没做好就会闪。这类问题一般只能等 DXMT 更新,或者去项目的 issue 列表里找有没有临时解决方案。
还有一个容易被忽略的点是色彩空间。Direct3D 默认用 sRGB 色彩空间,Metal 也支持 sRGB,但如果配置不对,画面会偏暗或者偏灰。在 DXMT 的环境变量里,DXMT_COLOR_SPACE可以指定色彩空间,一般设成srgb就行。如果显示器支持 HDR,还需要额外配置 HDR 相关的参数,否则 HDR 内容会被映射成 SDR,效果很差。
3.3 图形翻译链路的性能瓶颈在哪里
从 Direct3D 到 Metal 的翻译链路,性能瓶颈通常不在翻译本身,而在状态切换和资源绑定。Direct3D 的状态管理模型和 Metal 不一样,DXMT 需要在两者之间做映射。如果游戏频繁地切换渲染状态,比如改变混合模式、深度测试、剔除模式,DXMT 就要频繁地更新 Metal 的渲染管线状态,这个开销不小。
另一个瓶颈是着色器编译。Metal 的着色器是用 Metal Shading Language 写的,DXMT 需要把 Direct3D 的 HLSL 着色器翻译成 MSL,然后编译成 Metal 的二进制。这个翻译和编译过程在游戏加载时进行,如果着色器数量多,加载时间会很长。着色器缓存能缓解这个问题,但第一次运行或者更新驱动后,还是得重新编译。
我在 Apple Silicon 设备上跑 DXMT 的体验是,轻量级的 2D 游戏和简单的 3D 游戏基本能流畅运行,但大型 3D 游戏就比较吃力了。M1 级别的芯片跑一些老游戏还行,M2 Pro 以上会好一些,但和原生 Windows 机器比还是有差距。如果 Madeira 的目标是移动端 iOS 设备,那性能预期还要再降一档,毕竟移动芯片的散热和功耗限制摆在那里。
4. iOS 端的部署现实:开发者模式、签名和自动化
4.1 iOS 开发者模式到底解决了什么问题
热搜词里出现了“ios 开发者模式”和“ios 26.3.1 怎么开发者模式”,说明很多人卡在了这一步。iOS 从 16 开始,对开发者模式的管理越来越严格。以前装个测试包,信任一下证书就能跑,现在必须开启开发者模式,而且开启的方式因系统版本而异。
开发者模式的核心作用是允许设备运行未经 App Store 审核的代码。对于 Madeira 这类项目,如果要在 iOS 上跑 FEX-Emu 和 Wine 的组合,肯定不能走 App Store 分发,只能通过侧载或者企业证书的方式安装。这就必须开启开发者模式,否则系统会直接拒绝运行。
开启开发者模式的步骤在不同 iOS 版本里不太一样。较新的版本里,需要先在设置里找到“隐私与安全性”,然后往下翻到“开发者模式”,打开开关,设备会要求重启,重启后还要再确认一次。有些版本里这个选项是隐藏的,需要先用数据线连接 Xcode,或者用一些工具触发一下才会出现。热搜词里提到的“ios 26.3.1”如果是指某个未来的版本,那具体步骤可能还会变,但大方向是苹果在逐步收紧侧载权限。
4.2 签名和证书的坑
iOS 应用的签名机制是另一个大坑。免费开发者账号签名的应用只有 7 天有效期,过期后需要重新签名。付费开发者账号是 1 年。企业证书理论上可以签很多设备,但苹果对滥用企业证书的打击力度很大,证书随时可能被吊销。
如果 Madeira 要在 iOS 上长期运行,签名问题必须解决。常见的方案有几种:一是用 AltStore 或者 SideStore 这类工具,利用你的 Apple ID 自动续签;二是用 TrollStore,它利用了某些 iOS 版本的漏洞,可以永久签名,但只适用于特定版本;三是自己搭一个签名服务,用企业证书或者开发者证书来签。每种方案都有各自的限制和风险,选哪种取决于你的 iOS 版本和对稳定性的要求。
我个人的建议是,如果只是自己测试用,AltStore 这类自动续签工具就够了,虽然每 7 天要连一次电脑或者同一网络下的 AltServer,但至少不用手动折腾。如果要分发给其他人用,那就得考虑企业证书,但要做好证书随时失效的心理准备。TrollStore 虽然方便,但依赖系统漏洞,苹果随时可能在新版本里封堵,不适合长期依赖。
4.3 iOS 自动化和 WebView 相关的热搜词解读
热搜词里还有“ios 自动化”“抖音 ios webview 不能自动播放”“uniapp 使用 ios 原生插件”这些。这些词和 Madeira 的直接关联可能不大,但反映了关注这个项目的人群画像——很多是做移动端开发或者自动化测试的。iOS 的 WebView 自动播放限制是一个经典问题,苹果为了省电和用户体验,默认不允许 WebView 里的视频自动播放,需要用户手势触发。如果 Madeira 涉及在 iOS 上跑一些带界面的 Windows 程序,这些程序如果内嵌了 WebView,也会遇到同样的限制。
iOS 自动化方面,Xcode 自带的 XCTest 可以做 UI 测试,但需要应用有可访问性标签。如果 Madeira 跑的是 Windows 程序,这些程序没有 iOS 的可访问性支持,自动化测试就很难做。这种情况下,只能靠图像识别或者坐标点击的方式来做自动化,稳定性差很多。
5. 从热搜词反推:那些绕不开的实操问题
5.1 Wine 乱码和字体配置
“wine 乱码”和“wine 栏是乱码”这两个词出现频率很高,说明这是 Wine 使用中最常见的问题之一。Wine 乱码的根源是字体缺失或者字体映射不对。Windows 程序通常依赖微软雅黑、宋体、Arial 这些字体,但 Linux 或者 iOS 系统里不一定有这些字体,Wine 找不到就会用默认字体替代,如果默认字体不支持中文,就会显示成方块或者乱码。
解决办法是给 Wine 安装中文字体。可以把 Windows 的字体文件复制到 Wine 的字体目录里,或者用开源的字体替代,比如用 Noto Sans CJK 替代微软雅黑。具体操作是在 Wine 的注册表里配置字体替换规则,把Microsoft YaHei映射到Noto Sans CJK SC,把SimSun映射到Noto Serif CJK SC。这样 Windows 程序请求这些字体时,Wine 就会用对应的开源字体来渲染。
还有一个细节是 Wine 的“栏”乱码,这个“栏”可能指的是菜单栏、工具栏或者状态栏。这些 UI 元素的字体设置和普通文本不一样,它们通常由 Wine 的 theme 或者系统主题控制。如果主题里指定的字体不支持中文,栏上的文字就会乱码。解决方法是修改 Wine 的主题配置,把字体统一改成支持中文的字体。
5.2 麒麟 Wine 助手和统信 Wine 兼容组件
“麒麟 wine 助手”和“统信 wine windows 兼容组件下载”这两个词说明国内的信创生态里,Wine 也是一个重要的兼容方案。麒麟和统信都是国产 Linux 发行版,它们各自封装了 Wine 的配置工具,让用户能更方便地安装和运行 Windows 程序。这些工具本质上还是 Wine,只是加了图形化的配置界面和预配置的依赖库。
如果 Madeira 的目标平台包括这些国产 Linux 发行版,那适配工作就要考虑这些发行版的特殊性。比如麒麟的 Wine 助手可能对某些 DLL 做了替换,或者预装了一些 Windows 运行库。在 Madeira 里集成的时候,需要确认这些预配置会不会和 FEX-Emu 或者 DXMT 冲突。我遇到过的情况是,发行版自带的 Wine 版本比较老,和 FEX-Emu 要求的 Wine 版本不匹配,导致指令翻译层和 API 兼容层对不上,程序跑不起来。这种时候只能自己编译新版的 Wine,或者等发行版更新。
5.3 Xcode 打包慢和证书配置
“xcode 打包 ios 突然很慢如何解决”和“xcode 从证书配置到上架全流程”这两个词,说明有一部分关注者是在做 iOS 应用开发的。Xcode 打包慢的原因很多,常见的有:DerivedData 缓存过大、索引重建、代码签名验证慢、依赖库编译慢。清理 DerivedData 是最简单的办法,在 Xcode 的设置里找到 Locations,把 DerivedData 目录删掉,重新打包。如果还是慢,可以检查一下是不是开了 Address Sanitizer 或者 Thread Sanitizer,这些调试选项会显著拖慢编译和打包速度。
证书配置方面,iOS 的签名体系包括证书、App ID、设备列表、Provisioning Profile 几个部分。证书分开发证书和分发证书,开发证书用于真机调试,分发证书用于上架或者 Ad Hoc 分发。Provisioning Profile 把证书、App ID 和设备关联起来。如果打包时报签名错误,多半是 Provisioning Profile 里没有包含当前设备,或者证书过期了。在 Xcode 的 Accounts 设置里重新下载一下证书和 Profile,通常能解决大部分签名问题。
6. 把 Madeira 跑起来:一个可复现的配置思路
6.1 环境准备和依赖安装
假设我们要在一个 ARM64 的 Linux 环境里搭建 Madeira 的类似方案,目标是跑一个 x86-64 的 Windows 程序。首先需要安装 FEX-Emu,可以从源码编译,也可以用社区提供的二进制包。编译 FEX-Emu 需要 CMake、Ninja、Clang 这些工具,还要有 ARM64 的交叉编译工具链。如果是在 Apple Silicon 的 macOS 上,可以用 Homebrew 安装依赖,然后从源码编译。
Wine 的安装相对简单,大多数发行版都有现成的包。但要注意版本,FEX-Emu 对 Wine 的版本有一定要求,太老的版本可能不支持某些必要的补丁。我一般用 Wine 的 staging 分支,它包含了一些对游戏和图形程序更友好的补丁。DXMT 需要单独编译,它依赖 Metal 的头文件和框架,只能在 macOS 或者 iOS 上编译。
安装完这些组件后,需要配置环境变量。FEX_ROOTFS指向一个目录,这个目录里要有 Wine 的完整安装。WINEPREFIX指向 Wine 的 prefix 目录,每个 Windows 程序可以有自己的 prefix,避免相互干扰。DXMT_SHADER_CACHE指向一个可写的目录,用来缓存 Metal 着色器。
6.2 运行第一个 Windows 程序
配置好环境后,可以用FEX winecfg来启动 Wine 的配置工具,看看能不能正常跑起来。如果 winecfg 能打开,说明 FEX-Emu 和 Wine 的对接基本没问题。然后可以用FEX wine notepad.exe来跑一个简单的 Windows 程序,记事本是最简单的测试目标,如果记事本能正常显示和输入,说明基础环境是通的。
接下来可以试一个带图形界面的程序,比如一个老版本的 3D 游戏。运行的时候加上DXMT_D3D12=1和DXMT_METALFX=1这些环境变量,看看能不能正常渲染。如果画面出不来,先检查 DXMT 的日志,看看是着色器编译失败还是纹理格式不支持。日志一般在DXMT_LOG指定的路径下,或者直接输出到终端。
6.3 性能调优和稳定性验证
基础功能跑通后,下一步是调优。先测一下帧率,用游戏自带的 benchmark 或者第三方的帧率监测工具。如果帧率不理想,可以尝试调整 FEX-Emu 的配置,比如关掉 TSO、开启多块编译、调整缓存大小。DXMT 这边可以尝试不同的 MetalFX 模式,比如性能模式或者质量模式,看哪个更适合当前硬件。
稳定性验证也很重要。跑一个长时间的压力测试,比如让游戏在场景里自动跑一两个小时,看看会不会崩溃或者内存泄漏。FEX-Emu 和 DXMT 都是比较年轻的项目,长时间运行的稳定性还需要验证。如果遇到崩溃,先看日志里的调用栈,定位是 FEX-Emu 的问题还是 DXMT 的问题,然后去对应的项目提 issue 或者找已有的解决方案。
7. 一些踩过的坑和实际体会
FEX-Emu 的缓存目录默认在~/.cache/fex-emu,如果磁盘空间不够,缓存写不进去,程序会莫名其妙地变慢或者崩溃。我遇到过好几次,查了半天以为是兼容性问题,最后发现是磁盘满了。所以跑之前先确认一下缓存目录所在的分区有足够的空间,至少留几个 GB。
Wine 的字体配置在 FEX-Emu 环境下有时候不生效,原因是 FEX-Emu 对文件系统的路径映射和原生 Wine 有差异。Wine 以为字体装在某个路径下,但 FEX-Emu 翻译后的路径对不上,结果还是找不到字体。解决办法是在 Wine 的注册表里用绝对路径指定字体文件,或者把字体直接放到 Wine prefix 的drive_c/windows/Fonts目录下,这个路径在 FEX-Emu 下映射比较稳定。
DXMT 的着色器缓存在不同版本的 Metal 驱动之间不兼容。系统更新后,Metal 驱动版本变了,旧的着色器缓存可能失效,导致游戏重新编译着色器,加载时间变长。如果更新系统后发现游戏加载特别慢,可以试着清空 DXMT 的着色器缓存目录,让它重新编译。虽然第一次会慢,但之后就好了。
iOS 端的部署,最大的不确定性来自苹果的政策变化。今天能用的签名方式,明天可能就被封了。如果要在 iOS 上长期跑 Madeira,最好准备多套签名方案,一套失效了能快速切换到另一套。另外,iOS 的后台限制很严格,长时间运行的程序容易被系统挂起,需要配置好后台任务或者引导式访问,防止程序被意外中断。
最后说一个关于性能预期的体会。在 ARM 设备上跑 x86-64 的 Windows 程序,经过指令翻译和 API 翻译两层转换,性能损耗是客观存在的。不要指望能和原生 x86 机器一样快,能跑到原生 50% 到 70% 的性能就算不错了。如果是图形密集型的程序,这个比例可能更低。所以 Madeira 这类方案更适合跑一些对性能要求不高的老程序或者工具软件,大型游戏还是得用原生平台。