1. 项目缘起为什么我要折腾 Madeira 这套跨架构兼容方案第一次看到 Madeira 这个词很多人会以为是葡萄牙那个产葡萄酒的岛屿。但在我们这行里它指的是一套围绕FEX-Emu Wine DXMT搭建起来的跨架构运行环境方案目标很直接让 x86-64 的 Windows 应用和游戏能在 ARM 设备上跑起来而且尽量跑得流畅、跑得省心。我最初接触这套东西是因为手上有一台 ARM 架构的迷你主机性能不差、功耗极低但一碰到只发 x86-64 版本的 Windows 软件就抓瞎。虚拟机方案太重纯 Wine 又跑不动带 DirectX 的游戏直到把 FEX-Emu、Wine、DXMT 这三块拼在一起才算真正把这条路走通。先把这三个核心组件的关系讲清楚不然后面全是糊涂账。FEX-Emu负责的是指令集翻译它把 x86-64 的机器指令实时翻译成 ARM64 能执行的指令相当于一个同声传译让 ARM CPU 听懂 x86 的方言。Wine负责的是 API 翻译它把 Windows 的系统调用翻译成 Linux 能理解的调用相当于一个外交翻译官让 Windows 程序以为自己在 Windows 上。DXMT则是把 Direct3D 的调用翻译成 Metal在部分平台上或者对接 Vulkan 层专门解决图形渲染的问题。三者叠加才构成一条完整的x86-64 Windows 程序 → ARM Linux 运行的链路。这套方案解决的核心痛点很明确ARM 设备生态越来越丰富但大量存量 Windows 软件、尤其是游戏只有 x86-64 版本。重写不现实虚拟机性能损耗大云游戏又依赖网络。Madeira 这套组合的价值就在于它把翻译这件事拆成三层各司其职每一层都能单独调优最终在本地实现可用的兼容运行。它适合谁适合手里有 ARM 设备、想跑 Windows 软件或游戏的技术玩家也适合做兼容层开发、想理解指令翻译和 API 翻译原理的工程师。哪怕你只是想搞明白为什么我的 ARM 板子跑不了某个 exe这套思路也值得完整看一遍。我踩过的第一个坑就是以为装个 Wine 就完事了。结果一个简单的 DirectX 9 小游戏帧率个位数画面还花屏。后来才明白Wine 本身不负责指令翻译在 ARM 上它得靠 FEX-Emu 把 x86 指令转过来图形部分还得靠 DXMT 接管缺一层都跑不顺。所以这篇就把这三层怎么搭、怎么调、怎么排错一次讲透。2. 三层翻译架构的设计逻辑与选型考量2.1 为什么是 FEX-Emu 而不是 QEMU 全系统模拟指令翻译这条路主流有两大流派全系统模拟和用户态翻译。QEMU 的全系统模拟是把整个 x86 机器包括 CPU、内存、外设都模拟出来跑一个完整的 x86 操作系统再在里头跑程序。这种方式兼容性最好但性能损耗极大因为每一条指令都要经过完整的模拟层而且整个 guest OS 的开销都算在你头上。FEX-Emu 走的是用户态翻译路线。它不模拟整台机器而是作为一个加载器存在你直接执行一个 x86-64 的 ELF 可执行文件FEX-Emu 接管它的指令流把 x86-64 指令块翻译成 ARM64 指令块翻译结果还会缓存起来复用。这就省掉了 guest OS 的全部开销性能能提升一大截。代价是它依赖宿主 Linux 内核提供系统调用兼容性不如全系统模拟那么无脑。我实测下来的感受是对于大多数 Windows 应用和游戏FEX-Emu 的性能优势是决定性的。一个用 QEMU 全系统模拟只能跑 15 帧的场景换成 FEX-Emu 用户态翻译能到 40 帧以上。这个差距不是调参能弥补的是架构层面的。所以 Madeira 选 FEX-Emu 作为指令翻译层逻辑上完全站得住。注意FEX-Emu 对内核版本有要求建议 5.15 以上太老的内核可能缺少它依赖的某些系统调用特性会导致启动直接失败。2.2 Wine 在 ARM 上的角色变化与配置要点Wine 在 x86 平台上是个纯粹的 API 翻译层但在 ARM 平台上它的角色变得微妙。因为 Wine 本身也是编译成 ARM64 的它加载的 Windows PE 文件却是 x86-64 的这中间就需要 FEX-Emu 介入。所以实际运行时链路是这样的ARM64 的 Wine 进程启动加载 x86-64 的 PE 文件FEX-Emu 把 PE 里的 x86-64 指令翻译成 ARM64 执行Wine 负责把 Windows API 调用翻译成 Linux 调用。这里有个关键配置点Wine 的架构必须和 FEX-Emu 匹配。你得用 ARM64 版本的 Wine而不是 x86-64 版本的 Wine 再套一层翻译。后者会变成翻译套翻译性能直接崩掉。我一开始就犯过这个错装了个 x86-64 的 Wine结果 FEX-Emu 要翻译 Wine 本身再翻译 Wine 加载的程序帧率惨不忍睹。换成 ARM64 Wine 之后性能立刻正常。Wine 的版本选择也有讲究。太老的版本对新游戏支持差太新的版本可能引入回归问题。我一般建议用Wine 8.x 到 9.x 之间的稳定版配合 winetricks 装一些必要的运行库比如 vcrun、dotnet 的对应版本。另外Wine 的 prefix也就是那个模拟的 C 盘目录建议单独建别和系统混在一起出问题了好删了重来。2.3 DXMT 补齐图形渲染这最后一块拼图Wine 自带的图形翻译层是 WineD3D它把 Direct3D 调用翻译成 OpenGL。这条路在 x86 上勉强能用但在 ARM 上OpenGL 驱动本身就不如 x86 平台成熟再叠一层翻译性能和兼容性都堪忧。DXMT 的思路不一样它把 Direct3D 调用直接翻译成 Metal在支持 Metal 的平台上或者更底层的图形 API绕开了 OpenGL 这一层。DXMT 的核心价值在于减少翻译层级。WineD3D 是 D3D → OpenGL → 驱动DXMT 是 D3D → Metal/Vulkan → 驱动少了一层延迟和开销都降下来了。对于 DirectX 11 及以下的游戏DXMT 的提升尤其明显。我测过一个 DX11 的游戏用 WineD3D 跑 25 帧换 DXMT 之后稳定 45 帧以上画面撕裂也少了。不过 DXMT 不是万能的。它对 DirectX 12 的支持还在完善中部分新游戏可能还是得回退到 WineD3D 或者别的方案。而且 DXMT 对驱动版本有要求太老的 GPU 驱动可能不支持它依赖的某些特性。所以实际配置时我建议先确认你的 GPU 驱动版本再决定用不用 DXMT。组件负责层级核心作用性能影响FEX-Emu指令层x86-64 指令翻译成 ARM64决定基础性能上限WineAPI 层Windows API 翻译成 Linux 调用影响兼容性和启动速度DXMT图形层Direct3D 翻译成 Metal/Vulkan决定游戏帧率和画面质量3. 从零搭建 Madeira 环境的完整实操流程3.1 环境准备与依赖检查动手之前先把基础环境摸清楚。你需要一台 ARM64 架构的 Linux 设备内核版本 5.15 以上最好有独立的 GPU 或者至少支持 Vulkan 的集成显卡。内存建议 8GB 起步因为 FEX-Emu 的翻译缓存和 Wine 的 prefix 都挺吃内存。存储方面SSD 是必须的机械硬盘跑这套东西会卡到怀疑人生。第一步确认架构和内核uname -m uname -runame -m应该输出aarch64如果是x86_64那说明你根本不需要这套方案。uname -r确认内核版本低于 5.15 的建议先升级内核。第二步检查 GPU 和驱动lspci | grep -i vga glxinfo | grep OpenGL version vulkaninfo | grep apiVersionvulkaninfo如果报错说找不到命令先装vulkan-tools。DXMT 依赖 Vulkan这一步不能省。第三步装编译工具链。FEX-Emu 和 DXMT 很多时候需要自己编译所以build-essential、cmake、ninja-build、git这些都得备齐sudo apt update sudo apt install -y build-essential cmake ninja-build git python3 pkg-config提示不同发行版的包名可能不一样上面是 Debian/Ubuntu 系的写法。如果你用的是别的发行版把apt换成对应的包管理器即可。3.2 FEX-Emu 的编译与安装FEX-Emu 的安装有两种方式用现成的二进制包或者从源码编译。二进制包省事但可能不是最新版源码编译麻烦但能拿到最新特性和针对你设备的优化。我一般推荐先试二进制包跑不通再编译。二进制安装的话去 FEX-Emu 的官方发布页找对应你发行版的包。以 Debian 系为例下载.deb之后sudo dpkg -i fex-emu_*.deb sudo apt install -f装完之后验证一下FEX --version如果输出了版本号说明装好了。接下来要配置 FEX 的 rootfs也就是它运行 x86-64 程序时需要的 x86-64 库环境。FEX 提供了一个工具叫FEXRootFSFetcher直接跑它按提示操作就行FEXRootFSFetcher它会下载一个精简的 x86-64 根文件系统放到~/.fex-emu/RootFS/下面。这个过程比较久取决于网速耐心等。源码编译的话流程大概是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编译参数里-DCMAKE_BUILD_TYPERelease是关键Debug 版本性能差很多。-DENABLE_ASSERTIONSOFF关掉断言也能提升一点性能。-j$(nproc)用满所有核心加速编译。3.3 Wine 的 ARM64 版本部署与 prefix 初始化Wine 这块最省事的办法是用发行版自带的 ARM64 Wine 包。Debian/Ubuntu 系直接sudo apt install -y wine wine64 winetricks装完确认版本和架构wine --version file $(which wine)file命令应该显示ARM aarch64如果显示x86-64那就装错了得卸了重装 ARM64 版本。接下来初始化 Wine prefix。我强烈建议单独指定一个 prefix 目录别用默认的~/.wine方便管理和重置export WINEPREFIX~/madeira-wine export WINEARCHwin64 wineboot -uWINEARCHwin64表示创建一个 64 位的 prefix。wineboot -u会初始化这个 prefix第一次跑会弹一些窗口等它跑完就行。初始化完之后用 winetricks 装一些常用运行库winetricks -q vcrun2019 dotnet48 corefontsvcrun2019是很多程序依赖的 Visual C 运行库dotnet48是 .NET Framework 4.8corefonts装一些基础字体避免中文乱码。这里就涉及到热词里提到的wine 乱码问题了——中文乱码多半是字体缺失导致的装完 corefonts 再把系统里的中文字体复制到 Wine 的字体目录基本就能解决。cp /usr/share/fonts/truetype/*/your-chinese-font.ttf ~/madeira-wine/drive_c/windows/Fonts/3.4 DXMT 的集成与图形层切换DXMT 的安装相对独立它本质上是一组 DLL需要放到 Wine 能找到的目录里。从源码编译的话git clone https://github.com/3Shain/dxmt.git cd dxmt mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease .. make -j$(nproc)编译产物里会有d3d11.dll、dxgi.dll这些文件。把它们复制到 Wine prefix 的system32目录cp d3d11.dll dxgi.dll ~/madeira-wine/drive_c/windows/system32/然后配置 Wine 让它优先加载 DXMT 的 DLL而不是自带的 WineD3D。这通过WINEDLLOVERRIDES环境变量控制export WINEDLLOVERRIDESd3d11n,b;dxgin,bn,b的意思是先试原生native也就是 DXMT 的不行再用内置builtin也就是 WineD3D 的。这样既有 DXMT 的性能又有 WineD3D 的兜底。注意DXMT 和 WineD3D 的切换不是全局的不同游戏可能表现不一样。有的游戏用 DXMT 帧率高有的反而用 WineD3D 更稳。建议每个游戏单独测别一刀切。4. 性能调优与常见问题排查实录4.1 FEX-Emu 翻译缓存的调优技巧FEX-Emu 的性能很大程度上取决于翻译缓存的命中率。第一次运行某个程序时它要把 x86-64 指令翻译成 ARM64这个过程慢翻译结果缓存下来之后第二次运行就快很多。所以第一次跑别急着下结论多跑几次让缓存热起来。缓存的配置在~/.fex-emu/Config.json里。有几个参数值得调{ Config: { RootFS: Ubuntu_22_04, Emulation: { X87ReducedPrecision: true, TSOEnabled: true, Multiblock: true, SMCChecks: mtrack } } }TSOEnabled是内存序模拟开了对多线程程序兼容性更好但有一点性能开销。Multiblock开启多块翻译能提升性能。SMCChecks设成mtrack是自修改代码检测很多加壳的程序需要这个不然会崩溃。我实测下来Multiblock开启后某些游戏的帧率能提升 10% 到 15%。TSOEnabled则要看程序单线程的老游戏可以关掉换性能多线程的新程序建议开着保稳定。4.2 Wine 乱码与字体问题的系统化解决wine 乱码是热词里高频出现的问题我专门花时间系统排查过。乱码分几种情况菜单文字变成方块、中文显示成问号、界面文字重叠。根因基本都是字体缺失或者字体映射不对。解决思路分三步。第一步确认系统里有中文字体fc-list :langzh如果没有输出先装字体sudo apt install -y fonts-noto-cjk fonts-wqy-zenhei第二步把中文字体链接到 Wine 的字体目录ln -s /usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc ~/madeira-wine/drive_c/windows/Fonts/第三步配置 Wine 的字体替换规则。在 prefix 目录下建一个font_replacements.regcat ~/madeira-wine/font_replacements.reg EOF REGEDIT4 [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes] MS Shell DlgNoto Sans CJK SC MS Shell Dlg 2Noto Sans CJK SC SimSunNoto Sans CJK SC 宋体Noto Sans CJK SC EOF wine regedit ~/madeira-wine/font_replacements.reg这样 Wine 就会把 Windows 的默认字体映射到 Noto Sans CJK中文乱码基本能根治。4.3 游戏启动失败与画面异常的排查表实际跑游戏时问题五花八门。我整理了一张排查表按现象找原因效率高很多。现象可能原因排查方法解决方案启动直接闪退FEX 翻译失败看终端报错找SIGILL或SIGSEGV检查 FEX 版本更新到最新黑屏无画面图形层没接管看是否加载了 DXMT检查WINEDLLOVERRIDES配置帧率极低翻译缓存没热多跑几次看是否改善开启 Multiblock等缓存建立画面花屏驱动或 DXMT 不兼容换 WineD3D 测试回退到 WineD3D 或更新驱动中文乱码字体缺失fc-list :langzh检查装中文字体并配置替换声音异常音频后端问题检查 PulseAudio/PipeWire切换 Wine 音频驱动排查的核心思路是分层定位先确认 FEX 层有没有问题程序能不能启动再确认 Wine 层有没有问题API 调用有没有报错最后确认图形层有没有问题画面正不正常。一层一层排除比瞎试快得多。提示排查时把WINEDEBUG打开能看到详细的 API 调用日志。export WINEDEBUGd3d11,dxgi可以专门看图形相关的日志定位图形问题特别有用。4.4 我踩过的几个典型坑与独家经验第一个坑是内核版本不够。我一开始在一台老设备上折腾内核是 5.4FEX-Emu 死活启动不了报了一堆看不懂的错误。折腾半天才意识到是内核太老升级到 5.15 之后一次就过了。所以动手前先确认内核版本能省很多时间。第二个坑是Wine 架构装错。前面提过装了 x86-64 的 Wine导致翻译套翻译性能崩盘。判断方法很简单file $(which wine)一看便知。这个坑很隐蔽因为程序能跑就是慢容易误以为是性能问题其实是架构问题。第三个坑是DXMT 和 WineD3D 混用冲突。有次我同时配了 DXMT 和 WineD3D 的覆盖规则结果两个 DLL 打架游戏直接崩溃。后来才明白WINEDLLOVERRIDES里的规则要清晰别让同一个 DLL 有多个来源。要么全用 DXMT要么全用 WineD3D别混。第四个坑是翻译缓存目录权限问题。FEX 的缓存目录如果权限不对翻译结果写不进去每次运行都重新翻译性能一直上不去。检查~/.fex-emu/的权限确保当前用户可读写。5. 跨架构兼容方案的延展与个人体会5.1 从 Madeira 看兼容层的通用设计思路Madeira 这套方案的价值不只是让 ARM 跑 x86 程序这么简单它体现的是兼容层设计的一个通用范式分层翻译各司其职。指令层管指令API 层管 API图形层管图形每一层都可以独立替换和优化。这个思路可以迁移到很多场景。比如你在做跨平台的软件适配与其写一个大而全的兼容层不如拆成几个专注的小层每层解决一类问题。这样调试的时候能快速定位是哪一层出了问题优化的时候也能针对性地调。我在做其他兼容项目时也经常借鉴这个分层思路效果很好。另一个值得借鉴的点是缓存策略。FEX-Emu 把翻译结果缓存下来复用这是性能优化的关键。任何做实时翻译、实时转换的系统缓存都是绕不开的优化手段。缓存命中率上去了性能自然就上去了。5.2 不同 ARM 设备上的适配差异ARM 设备之间的差异比 x86 设备大得多。同样是 ARM64不同厂商的 CPU 在指令集扩展、缓存大小、内存带宽上差别很大这直接影响 FEX-Emu 的翻译效率和运行性能。我测过几台不同设备同样的游戏帧率能差一倍以上。适配的时候重点关注几个点CPU 是否支持某些 ARM64 扩展指令影响翻译质量、内存带宽够不够影响翻译缓存的读写速度、GPU 驱动是否成熟影响 DXMT 的发挥。这些在选设备的时候就要考虑别等装好了才发现跑不动。5.3 我个人在实际操作中的体会折腾 Madeira 这套东西最大的体会是耐心比技术重要。这套方案的文档不算完善很多问题得自己摸索报错信息也不一定友好。但只要按分层思路一步步排查大部分问题都能解决。另外别追求一步到位。先把 FEX-Emu 跑通确认能执行简单的 x86-64 程序再装 Wine确认能跑简单的 Windows 程序最后上 DXMT确认图形正常。一层一层来每层都验证通过再往下走。这样出问题的时候你能确定是哪一层的问题而不是一团乱麻。最后分享一个小技巧建一个测试脚本把环境变量和启动命令都写进去每次测试直接跑脚本避免手动敲命令出错。脚本里把WINEPREFIX、WINEARCH、WINEDLLOVERRIDES、FEX相关的配置都固定下来测试不同游戏时只改游戏路径就行。这个习惯帮我省了大量重复劳动也避免了环境变量不一致导致的诡异问题。这套方案后续还可以往几个方向扩展一是尝试接入更多的图形后端比如针对特定 GPU 优化的渲染路径二是优化翻译缓存的预加载让首次运行也不那么慢三是把这套环境容器化方便在不同设备间迁移。这些我都还在摸索有进展再分享。