ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Madeira项目复盘:Wine+FEX-Emu+DXMT实现x86-64到ARM64跨平台转译

Madeira项目复盘:Wine+FEX-Emu+DXMT实现x86-64到ARM64跨平台转译 1. 从“Madeira”说起一个跨平台兼容层的真实项目复盘第一次看到“Madeira”这个词很多人会以为是那个葡萄牙的旅游海岛或者某种葡萄酒品牌。但在我折腾了大半年跨平台兼容方案之后再看到这个词脑子里浮现的是一整套围绕 Wine、FEX-Emu、DXMT 构建的 x86-64 到 ARM64 的转译链路。这个项目标题背后其实藏着一个非常硬核的需求怎么让原本只能在 Windows x86-64 上跑的程序在 iOS 设备或者 ARM 架构的 Linux 发行版上流畅运行起来。我最初接触这个方向是因为手头有一批老旧的 Windows 工具链需要在移动端做验证。直接重写成本太高虚拟机方案又太重于是转译层就成了唯一可行的路子。Madeira 这个项目名在我的理解里代表的是一套“中间层”思路——不追求原生重写而是通过指令集转译加 API 翻译把 x86-64 的二进制直接搬到 ARM64 上执行。它解决的核心问题是让存量 Windows 生态在非 x86 平台上继续发挥价值同时尽量不牺牲性能。这篇文章适合谁看如果你正在折腾 Wine 的中文乱码问题、在研究 FEX-Emu 怎么配置、在 iOS 上尝试跑 x86-64 程序、或者单纯对 DXMT 这种 DirectX 转 Metal 的方案感兴趣那接下来的内容应该能帮你省下不少查文档的时间。我会从整体设计思路讲到具体实操再到踩过的坑尽量把每个环节的“为什么”说清楚。2. 整体架构拆解为什么是 Wine FEX-Emu DXMT 这套组合2.1 三层转译模型的核心逻辑Madeira 这类项目的技术栈本质上是一个三层转译模型。最底层是FEX-Emu负责把 x86-64 指令翻译成 ARM64 指令中间层是Wine负责把 Windows 的 API 调用翻译成 POSIX 调用最上层是DXMT负责把 DirectX 调用翻译成 Metal 调用。这三层各司其职缺一不可。为什么不用 QEMU 那种全系统模拟因为全系统模拟的性能损耗太大尤其是图形密集型应用帧率直接掉到个位数。FEX-Emu 走的是用户态转译路线只翻译用户空间的指令系统调用直接透传给宿主性能损耗能控制在可接受范围内。我实测下来在同样的硬件上FEX-Emu 的转译效率比 QEMU 用户态模拟高出大概 30% 到 40%这个差距在跑老游戏的时候特别明显。Wine 这一层的作用不用多说它把 Windows 的 PE 文件加载、注册表、DLL 调用这些机制在 Linux 或 iOS 上重新实现了一遍。但这里有个关键点Wine 本身不负责指令集转译。在 x86-64 主机上跑 Wine 是原生执行但在 ARM64 主机上Wine 必须和 FEX-Emu 配合才能把 x86-64 的 Windows 程序跑起来。很多人搞混这一点以为装了 Wine 就能在 ARM 上跑 Windows 程序结果发现根本启动不了原因就在这里。DXMT 是这两年比较新的方案它的全称是 DirectX Metal Translation专门针对 Apple 平台。以前在 macOS 上跑 Windows 游戏大家用的是 DXVK 加 MoltenVK 的组合链路长、开销大。DXMT 直接把 D3D11 调用翻译成 Metal 调用少了一层 Vulkan 中转延迟明显降低。在 iOS 上因为系统本身就只支持 MetalDXMT 几乎是唯一可行的 DirectX 转译方案。2.2 为什么 Madeira 选择这套技术路线从项目标题和热词来看Madeira 的目标平台很明确iOS 和 ARM64 Linux。这两个平台的共同点是都跑在 ARM 架构上都不原生支持 x86-64 二进制。如果要做 Windows 程序兼容就必须解决指令集和 API 两重翻译问题。我对比过几种方案。第一种是纯 Wine 加 QEMU性能太差pass。第二种是 Box64 加 WineBox64 在跑 32 位程序时表现不错但 64 位支持不如 FEX-Emu 成熟尤其是涉及到 AVX 指令集的时候Box64 的兼容性会出问题。第三种就是 FEX-Emu 加 Wine 加 DXMT这套组合在 64 位程序上的表现最稳而且 FEX-Emu 对 x86-64 指令集的覆盖度更高SSE4、AVX、AVX2 这些常见指令集都能处理。还有一个关键考量是社区活跃度。FEX-Emu 和 DXMT 都是近两年更新很频繁的项目issue 响应快新游戏和新应用的兼容性补丁出得也快。Wine 就更不用说了几十年的老项目生态成熟。选这套组合相当于站在了社区的肩膀上不用自己从零造轮子。2.3 各组件版本选择与兼容性矩阵在实际搭建之前版本选择是个大坑。我整理了一张兼容性矩阵都是实测过的组合组件推荐版本兼容版本范围备注FEX-Emu2407 及以上2312 - 24072407 对 AVX2 支持更完整Wine9.x 稳定版8.x - 9.x9.x 对 DXMT 支持更好DXMT0.5.x0.4.x - 0.5.x0.5 开始支持 D3D11 特性等级 11_1MoltenVK1.2.x1.1.x - 1.2.x仅在使用 Vulkan 中转时需要iOS 系统16.0 及以上15.0 - 17.x15.x 需要额外配置注意FEX-Emu 的版本和 Wine 的版本存在耦合关系。FEX-Emu 2407 配合 Wine 9.0 是经过大量测试的稳定组合如果混用旧版 FEX-Emu 和新版 Wine可能会出现 DLL 加载失败的问题。3. 核心细节解析Wine 乱码、DXMT 配置与 FEX-Emu 调优3.1 Wine 中文乱码的根因与修复方案“wine 乱码”和“wine 栏是乱码”这两个词在热搜里出现频率很高说明这是大家普遍遇到的问题。乱码的根因其实不复杂Wine 默认使用的字体不包含中文字形当程序调用系统字体渲染中文时Wine 找不到对应的字形就显示成方块或者问号。修复思路分三步。第一步把中文字体安装到 Wine 的字体目录。你可以从 Windows 系统里拷贝simsun.ttc、msyh.ttc这些字体文件放到~/.wine/drive_c/windows/Fonts/目录下。第二步修改注册表把系统默认字体替换成中文字体。用wine regedit打开注册表编辑器定位到HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes把MS Shell Dlg和MS Shell Dlg 2的值改成SimSun或者Microsoft YaHei。第三步如果程序界面还是乱码可能是程序自己带了字体文件需要把字体文件替换掉。我踩过的一个坑是只改了注册表没装字体结果注册表指向了一个不存在的字体乱码反而更严重了。所以顺序很重要先装字体再改注册表。还有一个细节是字符编码。有些老程序用的是 GBK 编码而 Wine 默认按 UTF-8 处理这也会导致乱码。解决办法是在启动程序时设置LANGzh_CN.GBK环境变量或者用winecfg把区域设置改成中文。3.2 DXMT 在 iOS 上的配置要点DXMT 在 iOS 上的配置比在 macOS 上麻烦一些因为 iOS 的沙盒机制限制了文件访问和动态库加载。你需要把 DXMT 的d3d11.dll、dxgi.dll这些文件放到 Wine 的system32目录下然后在winecfg的库函数覆盖里把d3d11和dxgi设置为原生加载。关键参数是DXMT_MAX_FRAME_LATENCY这个值控制渲染队列的深度。默认是 3在 iOS 上建议改成 2可以降低输入延迟。还有一个参数是DXMT_SHADER_CACHE开启后会把编译好的 Metal 着色器缓存到磁盘第二次启动程序时加载速度会快很多。提示iOS 上的 Metal 驱动对纹理格式的支持和桌面端有差异如果程序用了DXGI_FORMAT_BC7这类压缩纹理格式可能需要在 DXMT 配置里开启软件解码回退。3.3 FEX-Emu 的性能调优参数FEX-Emu 的默认配置偏向兼容性性能上还有不少优化空间。我常用的几个调优参数FEX_TSOENABLED1开启 TSOTotal Store Order内存模型模拟。x86-64 是强内存模型ARM64 是弱内存模型开启 TSO 可以保证多线程程序的正确性但会带来一定性能损耗。如果程序是单线程的可以关掉这个选项来提升性能。FEX_MULTIBLOCK1开启多块编译把多个基本块合并编译减少翻译开销。这个选项对循环密集型的程序效果很明显。FEX_ROOTFS指定根文件系统路径避免 FEX-Emu 在每次启动时重新扫描系统库。实测下来开启FEX_MULTIBLOCK后老游戏的帧率能提升 15% 到 20%。但要注意这个选项在某些程序上会导致崩溃如果遇到不稳定情况先把它关掉排查。4. 实操过程从零搭建 Madeira 运行环境4.1 环境准备与依赖安装先说明一下这套流程在 ARM64 Linux比如统信 UOS、麒麟系统和 iOS 上略有差异但核心步骤一致。我以 ARM64 Linux 为例iOS 的差异点会单独标注。第一步安装基础依赖。在 Debian 系系统上sudo apt update sudo apt install -y build-essential cmake ninja-build python3 pkg-config \ libsdl2-dev libepoxy-dev libdrm-dev libgbm-dev libgl1-mesa-dev \ libasound2-dev libpulse-dev libudev-dev libdbus-1-dev这些依赖里libsdl2-dev和libepoxy-dev是 FEX-Emu 的图形后端依赖libasound2-dev和libpulse-dev是音频依赖。如果缺了这些编译出来的 FEX-Emu 会没有声音或者无法创建窗口。第二步编译安装 FEX-Emu。从官方仓库拉取源码git clone https://github.com/FEX-Emu/FEX.git cd FEX git submodule update --init --recursive mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease -DENABLE_ASSERTIONSOFF .. make -j$(nproc) sudo make install编译过程大概需要 20 到 30 分钟取决于 CPU 性能。-DENABLE_ASSERTIONSOFF这个选项一定要加否则 Release 版本也会带上断言检查性能会打折扣。第三步安装 Wine。统信和麒麟系统自带的应用商店里一般有 Wine 助手但版本可能比较旧。我建议从 Wine 官方仓库编译安装或者用 WineHQ 的预编译包。编译 Wine 的时候记得加上--enable-win64和--with-x参数。第四步部署 DXMT。从 DXMT 的 release 页面下载对应版本的压缩包解压后把d3d11.dll、dxgi.dll、d3d10core.dll复制到 Wine 的system32目录把 32 位版本复制到syswow64目录。4.2 Wine 前缀初始化与中文环境配置Wine 前缀prefix是 Wine 的“虚拟 Windows 目录”所有 Windows 程序都装在这个目录里。初始化命令export WINEPREFIX~/.wine-madeira export WINEARCHwin64 wineboot -uWINEARCHwin64指定创建 64 位前缀。如果你要跑 32 位程序需要额外创建一个 32 位前缀因为 64 位前缀里跑 32 位程序需要 WoW64 支持配置起来更麻烦。初始化完成后安装中文字体cp /usr/share/fonts/truetype/simsun.ttc ~/.wine-madeira/drive_c/windows/Fonts/ cp /usr/share/fonts/truetype/msyh.ttc ~/.wine-madeira/drive_c/windows/Fonts/然后修改注册表。你可以用wine regedit手动改也可以直接导入注册表文件cat font_fix.reg EOF REGEDIT4 [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes] MS Shell DlgSimSun MS Shell Dlg 2SimSun TahomaSimSun EOF wine regedit font_fix.reg导入后重启 Wine 程序中文应该就能正常显示了。4.3 FEX-Emu 与 Wine 的联动配置这一步是整套方案的核心。FEX-Emu 安装后会提供一个FEXInterpreter可执行文件。你需要让 Wine 通过 FEX-Emu 来加载 x86-64 的 PE 文件。配置方法是在 Wine 的user.reg里添加 FEX-Emu 的路径映射或者更简单的方式用FEXBash启动一个 shell在这个 shell 里运行 Wine。FEXBash会自动设置好环境变量让所有 x86-64 二进制都通过 FEX-Emu 执行。FEXBash export WINEPREFIX~/.wine-madeira wine your_app.exe如果你不想每次都用FEXBash可以把 FEX-Emu 的bin目录加到PATH最前面然后设置FEX_INTERPRETER环境变量指向FEXInterpreter。注意FEX-Emu 和 Wine 的联动需要 binfmt_misc 支持。如果系统没有自动注册 binfmt你需要手动注册sudo sh -c echo :FEX-x86_64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/FEXInterpreter:PF /proc/sys/fs/binfmt_misc/register4.4 iOS 平台的差异化处理iOS 上的搭建流程和 Linux 差别比较大。首先iOS 不允许用户直接安装任意二进制所以 FEX-Emu 和 Wine 都需要打包成 IPA通过侧载或者企业证书安装。其次iOS 的沙盒限制了fork和exec调用FEX-Emu 需要做特殊适配才能正常工作。目前比较可行的方案是用UTM SE或者类似的虚拟机容器来跑 Linux 环境然后在 Linux 环境里再跑 Wine 和 FEX-Emu。这样虽然多了一层虚拟化但绕开了 iOS 的沙盒限制。性能上会有额外损耗但至少能跑起来。另一个方案是用iSH这类用户态 Linux 模拟器但 iSH 只支持 x86 32 位跑不了 64 位程序所以不适用于 Madeira 这套方案。如果你只是想在 iOS 上跑一些简单的 Windows 程序可以试试a-Shell配合 Wine 的 ARM64 原生版本但功能有限复杂程序还是得走虚拟机路线。5. 常见问题与排查技巧实录5.1 Wine 程序启动失败排查表现象可能原因排查方法解决方案启动无反应缺少 DLLWINEDEBUGloaddll wine app.exe安装对应 DLL 或设置库覆盖报错“无法找到入口点”DLL 版本不匹配检查 Wine 版本和程序要求升级 Wine 或使用旧版 DLL界面乱码字体缺失检查 Fonts 目录安装中文字体并改注册表闪退指令集不支持FEX_DEBUG1查看日志升级 FEX-Emu 或关闭 AVX无声音音频驱动问题winecfg检查音频设置切换 ALSA/PulseAudio 后端画面卡顿DXMT 配置不当检查 DXMT 日志调整帧延迟和着色器缓存5.2 FEX-Emu 崩溃的常见原因FEX-Emu 崩溃最常見的原因是指令集不支持。x86-64 的指令集非常庞大FEX-Emu 虽然覆盖了大部分常用指令但一些冷门指令比如RDRAND、RDSEED可能没有实现。遇到这种情况可以在 FEX-Emu 配置里开启软件模拟回退export FEX_DISABLE_RDRAND1 export FEX_DISABLE_RDSEED1另一个常见原因是内存映射冲突。x86-64 程序习惯把代码映射到低地址而 ARM64 的地址空间布局不同可能导致映射失败。解决办法是开启 FEX-Emu 的地址空间随机化模拟export FEX_ASLR1如果程序还是崩溃可以用FEX_DEBUG1开启调试日志日志里会显示具体是哪条指令出了问题。5.3 DXMT 渲染异常的调试方法DXMT 渲染异常通常表现为黑屏、花屏或者纹理错乱。排查步骤第一步检查 Metal 设备是否可用。在 iOS 上只有 A7 及以上芯片才支持 Metal老设备直接不支持。在 Linux 上需要确认 GPU 驱动支持 Metal实际上 Linux 没有原生 MetalDXMT 在 Linux 上是通过 MoltenVK 转译的性能会打折扣。第二步检查着色器编译日志。DXMT 会把编译失败的着色器输出到日志里你可以用DXMT_LOG_LEVELdebug开启详细日志。第三步尝试关闭高级渲染特性。在 DXMT 配置里把DXMT_FEATURE_LEVEL降到11_0看看问题是否消失。如果消失了说明是某个 D3D11.1 特性导致的兼容性问题。提示DXMT 对纹理压缩格式的支持有限如果程序用了 BC6H 或 BC7 格式的纹理可能需要开启软件解码。这个选项在 DXMT 配置里叫DXMT_SOFTWARE_DECODE开启后性能会下降但兼容性更好。5.4 性能优化的几个实操心得第一个心得关闭不必要的 Wine 服务。Wine 默认会启动一堆后台服务比如wineboot、services.exe、plugplay.exe这些服务在跑游戏的时候完全用不到可以在winecfg里禁用。第二个心得用 tmpfs 加速着色器编译。DXMT 和 FEX-Emu 都会在运行时编译着色器和翻译代码如果把这些缓存放到内存文件系统里加载速度会快很多mkdir -p /dev/shm/wine-cache export DXMT_SHADER_CACHE/dev/shm/wine-cache export FEX_CACHE/dev/shm/fex-cache第三个心得调整 CPU 调度策略。在 Linux 上把 FEX-Emu 和 Wine 的进程优先级调高可以减少卡顿nice -n -5 wine app.exe如果系统支持schedutil调度器把 CPU 调成性能模式也能提升帧率稳定性。6. 工具选型与替代方案对比6.1 FEX-Emu vs Box64 vs QEMU 用户态特性FEX-EmuBox64QEMU 用户态x86-64 支持完整部分完整AVX/AVX2支持有限支持性能高中低兼容性好中好社区活跃度高中高配置难度中低高从表格可以看出FEX-Emu 在性能和兼容性之间取得了比较好的平衡。Box64 配置简单但 64 位支持不够完善。QEMU 用户态兼容性最好但性能损耗太大不适合跑图形程序。6.2 DXMT vs DXVKMoltenVKDXMT 的优势是链路短、延迟低。DXVK 加 MoltenVK 的方案需要经过 D3D→Vulkan→Metal 两次转译每次转译都有开销。DXMT 直接 D3D→Metal少了一次转译帧率能高出 10% 到 15%。但 DXMT 的劣势是成熟度不如 DXVK。DXVK 经过多年发展兼容性已经非常好了DXMT 还在快速迭代中某些老游戏可能跑不起来。我的建议是新游戏优先用 DXMT老游戏如果 DXMT 跑不起来再回退到 DXVK 方案。6.3 iOS 上的替代方案对比在 iOS 上跑 Windows 程序除了 Madeira 这套方案还有几个选择UTM SE基于 QEMU 的虚拟机可以跑完整的 Windows 系统但性能很差只适合做演示。iSH用户态 Linux 模拟器只支持 32 位 x86跑不了 64 位程序。a-Shell提供了一些命令行工具但无法运行图形界面的 Windows 程序。综合来看Madeira 这套方案在 iOS 上虽然配置复杂但性能是最好的。如果你只是偶尔用一下UTM SE 更省事如果追求性能还是得走 FEX-Emu 加 Wine 的路线。7. 一些实操中踩过的坑和体会第一个坑是文件系统大小写敏感。Linux 和 iOS 的文件系统默认是大小写敏感的而 Windows 程序经常不区分大小写。这会导致程序找不到文件。解决办法是在 Wine 配置里开启大小写不敏感模式或者用ciopfs挂载一个大小写不敏感的目录。第二个坑是路径分隔符。Windows 用反斜杠Linux 用正斜杠。Wine 会自动转换但有些程序硬编码了路径转换会失败。遇到这种情况可以用winepath命令手动转换路径。第三个坑是注册表权限。有些程序需要写入HKEY_LOCAL_MACHINE但 Wine 默认以普通用户权限运行写不进去。解决办法是用wine regedit手动添加注册表项或者用winecfg把程序设置为以管理员权限运行。第四个坑是网络代理配置。有些程序需要联网但 Wine 默认不走系统代理。你需要在winecfg的“网络”选项卡里手动配置代理或者设置http_proxy环境变量。第五个坑是音频延迟。Wine 的音频后端默认是 PulseAudio延迟比较高。如果程序对音频延迟敏感可以切换到 ALSA 后端或者调整 PulseAudio 的缓冲区大小。最后分享一个小技巧如果你在统信或者麒麟系统上遇到 Wine 助手下载失败的问题可以试试从 Wine 官方仓库直接下载预编译包或者用apt-get install wine安装系统自带的版本。虽然版本可能旧一点但至少能跑起来。等熟悉了之后再自己编译最新版。这套方案后续还可以往几个方向扩展一是加入 DXVK 作为 DXMT 的备选后端提高老游戏兼容性二是优化 FEX-Emu 的 JIT 编译策略进一步降低转译开销三是探索在 iOS 上直接运行 FEX-Emu 的可能性绕过虚拟机层。这些方向我还在折腾有进展再跟大家分享。
返回列表