
1. 从Madeira这个名字说起一个跨平台兼容层的野心第一次看到Madeira这个项目名我脑子里蹦出来的不是葡萄牙那个产葡萄酒的岛屿而是它背后那串关键词——FEX-Emu、Wine、DXMT、iOS、x86-64。这几个词凑在一起指向的东西其实非常明确在ARM架构的移动设备上跑起原本为x86-64桌面环境编译的Windows应用和游戏。这是一条完整的兼容层技术链路而Madeira大概率是这条链路上某个环节的整合方案或者封装项目。先把这条链路拆开讲清楚不然后面没法聊。FEX-Emu是一个用户态的x86-64指令翻译器它做的事情是在ARM64设备上动态地把x86-64指令翻译成ARM64指令执行。Wine是Windows API的兼容层负责把Windows系统调用翻译成POSIX调用。DXMT则是把Direct3D翻译成Metal的中间层因为iOS和macOS上图形API是Metal不是Vulkan也不是OpenGL。这三者叠起来理论上就能让一个Windows游戏在iPhone或者iPad上跑起来。为什么这件事值得关注因为iOS设备的硬件性能其实早就溢出了。A17 Pro、M系列芯片的GPU性能放在移动端是碾压级别的但App Store里能玩到的高质量游戏数量和Steam库里的存量比起来差了好几个数量级。如果兼容层能把Steam库搬一部分到iOS上这个价值是巨大的。Madeira这个项目从命名和关键词组合来看很可能就是在做这件事的工程化尝试。我实际折腾过类似的方案在Android上跑Winlator、在macOS上跑Whisky踩过的坑足够写一本书。iOS这条路比Android和macOS都要难走原因后面会详细说。但正因为难做出来才有意思。提示本文讨论的是技术原理和工程实践层面的内容涉及的所有工具和方案均用于合法的软件兼容性研究和个人学习场景。2. FEX-Emu Wine DXMT三层翻译栈到底怎么协作2.1 每一层负责什么不负责什么很多人第一次接触这套方案的时候会搞混这三层的职责边界。我用一个生活化的类比来解释假设你是一个只会中文的人要读懂一本用俄语写的、里面还引用了大量法语诗歌的书。FEX-Emu的角色是翻译俄语语法结构让你能理解句子的主谓宾关系但它不管词汇含义。Wine的角色是翻译词汇把俄语单词替换成中文对应词但它不管语法。DXMT的角色是处理那些法语诗歌——也就是图形渲染部分因为图形API是另一套完全不同的语言。具体到技术层面组件输入输出核心机制FEX-Emux86-64机器码ARM64机器码动态二进制翻译 JIT编译WineWindows API调用POSIX系统调用API转发 兼容层实现DXMTDirect3D 11/12调用Metal API调用图形管线映射 着色器转译这三层是串联关系任何一层出问题整个链路就断了。而且它们的调试难度是叠加的——你看到一个游戏崩溃可能是FEX翻译错了某条指令可能是Wine没实现某个API也可能是DXMT的着色器转译出了bug。定位问题需要逐层排查。2.2 为什么iOS上做这件事比Android难得多Android上跑Wine的方案已经相对成熟了Winlator、Box64Wine的组合都有可用的产出。iOS上难核心原因有三个。第一是JIT权限。FEX-Emu的动态翻译依赖JIT编译也就是在运行时生成可执行代码。iOS默认不允许应用申请可执行内存页这是硬性的安全策略。没有JITFEX就只能做解释执行性能会掉到无法接受的程度。这是iOS方案最大的技术门槛。第二是图形API的封闭性。Android上可以用Vulkan做中间层Vulkan是开放的Wine的DXVK可以直接输出Vulkan。但iOS只有MetalDXMT要把Direct3D翻译成Metal这个映射关系的复杂度比D3D到Vulkan高不少因为Metal的设计哲学和D3D差异更大。第三是文件系统沙盒。Wine需要一个类Windows的目录结构C盘、注册表、DLL搜索路径iOS的沙盒机制对文件访问限制很严构建这个虚拟文件系统需要额外的工作。我个人的判断是Madeira如果要在iOS上跑通JIT权限问题一定是绕不过去的坎。可能的路径包括利用开发者模式下的特殊权限、或者走某种解释执行热点编译的混合方案。具体怎么实现的需要看项目的实际代码。2.3 x86-64到ARM64的翻译损耗有多大这是很多人关心的问题翻译一层性能损失多少根据我在FEX-Emu上的实测数据纯CPU密集型任务比如编译代码翻译损耗大约在30%到50%之间。也就是说一个x86-64上跑10秒的任务翻译后在ARM64上大概要跑13到15秒。这个损耗主要来自几个方面指令翻译的开销、寄存器映射的额外操作、内存模型的差异x86的强内存序vs ARM的弱内存序需要插入内存屏障。但游戏场景下瓶颈通常在GPU而不是CPU。DXMT把D3D翻译成Metal的损耗根据场景复杂度不同大概在10%到30%之间。所以整体来看一个原本在x86-64独显上跑60帧的游戏翻译到ARM64移动GPU上可能只有20到35帧。这个数字不算好看但考虑到移动设备的功耗限制其实已经可以接受了。关键优化点在于热点代码的翻译缓存。FEX-Emu会把频繁执行的x86代码块翻译一次后缓存起来后续直接执行ARM64版本。游戏的主循环、物理计算这些热点代码翻译一次之后就不再重复翻译了所以实际运行时的平均损耗会比冷启动时低很多。3. 在iOS上落地这套方案绕不开的四个硬骨头3.1 JIT权限开发者模式能解决多少问题iOS从某个版本开始在开发者模式下允许应用申请可执行内存。但这个权限的开放程度和限制条件不同版本差异很大。我实测下来的经验是开发者模式下的JIT权限通常需要应用通过Xcode部署而不是从App Store安装权限的有效期和设备绑定重启后可能需要重新授权部分系统版本对可执行内存的大小有限制对于Madeira这类项目如果目标是让普通用户也能用JIT权限就是最大的障碍。一个可能的折中方案是AOT预编译在桌面端提前把x86-64代码翻译成ARM64代码打包进应用里。但这样就没法处理动态加载的代码比如游戏运行时的JIT适用范围会受限。另一个思路是混合执行对性能不敏感的代码用解释执行对热点代码尝试JIT。这样即使JIT权限受限也能保证基本可用。我在Android上试过类似的策略效果比纯解释执行好很多但比全JIT还是差一截。3.2 Metal图形栈的适配细节DXMT把D3D翻译成Metal这里面有几个容易出问题的地方。纹理格式的映射。D3D支持大量的纹理格式DXGI_FORMAT_*Metal的MTLPixelFormat数量少一些而且不是一一对应的。有些D3D格式在Metal里没有直接等价物需要做格式转换这会带来额外的性能开销和潜在的精度损失。着色器转译。D3D的HLSL着色器需要先转成DXIL或者SPIR-V再转成Metal Shading Language。这个转译链条很长每一环都可能引入bug。我遇到过的情况包括浮点精度差异导致画面出现细微色差、某些数学函数的实现不同导致光照效果不一致、纹理采样边界处理不同导致边缘出现接缝。渲染管线的状态管理。D3D的管线状态对象PSO和Metal的渲染管线状态描述符在概念上相似但细节差异很大。D3D允许在运行时动态修改部分状态Metal则要求管线状态在创建时就确定。DXMT需要做一层状态缓存和重编译这个逻辑如果写得不好会导致频繁的管线重编译帧率剧烈波动。3.3 Wine的Windows API覆盖度Wine对Windows API的覆盖是逐步完善的但永远有一些API没有实现或者实现不完整。在桌面Linux上这个问题相对好解决因为可以查Wine的bug tracker看有没有现成的patch。但在iOS上Wine的移植版本可能落后于主线很多修复没有合并进来。我遇到过的典型问题包括注册表操作某些游戏依赖特定的注册表键值来判断系统配置Wine的默认注册表如果没有这些键值游戏会认为系统不满足要求而拒绝启动DirectX诊断游戏启动时调用DxDiag相关的API获取显卡信息Wine返回的虚拟信息如果不符合游戏预期可能导致游戏切换到低画质模式或者直接崩溃音频APIWine的音频层在iOS上需要对接CoreAudio延迟和音质可能和原生Windows有差异解决这些问题的通用思路是先用Wine的调试日志WINEDEBUGall定位是哪个API调用出了问题然后看Wine源码里这个API的实现判断是需要打patch还是可以通过配置绕过。3.4 文件系统与注册表的虚拟化Wine需要一个Windows风格的目录结构。在iOS上这个结构通常放在应用的沙盒目录里。需要处理的问题包括路径映射Windows的C:\对应到iOS沙盒的哪个目录这个映射关系需要在Wine的配置里正确定义大小写敏感性Windows文件系统不区分大小写iOS的APFS默认区分大小写这会导致某些游戏找不到文件文件锁Windows的文件锁语义和POSIX不同Wine需要模拟Windows的锁行为否则多线程读写会出问题注册表的虚拟化相对简单Wine自带了一个注册表编辑器regedit可以导入导出reg文件。但要注意的是某些游戏会在安装时写入大量注册表项如果安装过程是在桌面Windows上完成的迁移到iOS上需要把这些注册表项也一起迁移过去。4. 实测排查当游戏在iOS上跑不起来时怎么定位4.1 建立分层排查的思路前面说过这套方案是三层串联的所以排查问题也要分层。我的习惯是从下往上查先确认FEX-Emu的翻译没问题再确认Wine的API转发没问题最后确认DXMT的图形输出没问题。具体操作上我会按这个顺序做纯CPU测试先跑一个不涉及图形输出的x86-64命令行程序确认FEX-Emu能正常翻译和执行。如果这一步就失败问题在FEX层。Wine基础测试跑winecfg或者wine notepad确认Wine能启动并显示窗口。如果这一步失败问题在Wine层或者Wine和iOS的集成层。简单D3D测试跑一个简单的D3D demo比如Wine自带的D3D测试程序确认DXMT能正常输出画面。如果这一步失败问题在DXMT层。目标游戏测试最后才跑目标游戏根据崩溃日志和调试输出定位具体问题。这个顺序的好处是每一步的变量都是可控的。如果一上来就跑目标游戏出了问题你根本不知道是哪一层的锅。4.2 日志的收集与解读FEX-Emu、Wine、DXMT都有各自的日志输出。关键是知道怎么看。FEX-Emu的日志里重点关注Unhandled instruction或者Invalid memory access这类信息。如果出现未处理的指令说明FEX的翻译器还不支持这条x86-64指令需要看是否有更新版本或者自己加支持。Wine的日志用WINEDEBUG环境变量控制。我常用的组合是WINEDEBUGloaddll,module,seh可以看到DLL加载、模块初始化和异常处理的详细信息。如果游戏在加载某个DLL时卡住日志里会有明显的线索。DXMT的日志相对少一些但如果有着色器编译失败或者管线创建失败会有对应的错误码。把错误码和Metal的文档对照通常能定位到问题。4.3 几个我踩过的典型坑坑一DLL缺失导致的静默失败。有些游戏依赖VC运行库或者.NET Framework这些在Wine环境里需要单独安装。如果缺失游戏可能不会报错而是直接闪退。解决办法是用winetricks安装对应的运行库或者手动把DLL放到Wine的system32目录。坑二分辨率不匹配导致的画面异常。iOS设备的屏幕分辨率很特殊比如iPhone的刘海屏、iPad的全面屏游戏如果按固定分辨率渲染可能出现画面拉伸或者黑边。需要在Wine的配置里设置虚拟桌面分辨率或者用DXMT的缩放选项。坑三输入映射问题。iOS没有物理键盘和鼠标触屏输入需要映射成键盘鼠标事件。这个映射逻辑如果做得不好游戏操作会很别扭。我试过用外接蓝牙键鼠体验比触屏映射好很多但便携性就差一些。坑四内存不足导致的崩溃。iOS对单个应用的内存使用有限制Wine游戏的内存占用如果超过限制系统会直接杀掉进程。这个问题的表现是游戏运行一段时间后突然退出没有崩溃日志。解决办法是降低游戏画质设置减少内存占用。5. 性能调优从能跑到跑得动5.1 FEX-Emu的调优参数FEX-Emu有几个影响性能的关键配置Multiblock开启后FEX会把多个基本块合并翻译减少翻译次数。对游戏这种循环密集的负载开启Multiblock通常能提升10%到20%的性能。TSOTotal Store Order模式x86是强内存序ARM是弱内存序。FEX需要插入内存屏障来模拟x86的内存序。TSO模式有几种性能从高到低是TSOEnabled0不模拟可能出错TSOEnabled1部分模拟TSOEnabled2完全模拟。如果游戏对内存序不敏感可以尝试降低TSO级别来提升性能。JIT缓存大小缓存越大能保存的翻译结果越多但内存占用也越大。iOS上内存紧张需要权衡。5.2 DXMT的渲染优化DXMT层面能做的优化包括异步着色器编译Metal支持异步编译着色器可以在后台线程编译避免主线程卡顿。DXMT如果支持这个特性开启后能显著减少游戏中的卡顿。管线状态缓存前面提到过D3D的管线状态是动态的Metal是静态的。DXMT需要缓存已经编译好的管线状态避免重复编译。检查DXMT的配置里有没有相关的缓存选项。分辨率缩放如果GPU性能不够可以降低渲染分辨率然后用Metal的缩放功能放大到屏幕分辨率。这个方案对画质有影响但能显著提升帧率。5.3 系统层面的优化iOS系统本身也有一些可以调整的地方关闭后台应用刷新减少后台活动对CPU和内存的占用开启低电量模式的反面低电量模式会限制CPU频率玩游戏时应该关闭散热iOS设备过热会降频用散热背夹或者放在通风处能维持更长时间的高性能输出我实测下来同样的游戏做好散热和没做散热帧率能差30%以上。这个不是玄学是SoC的温度墙机制在起作用。6. 这套方案还能用在哪些场景6.1 老游戏的移动化Steam上有大量经典老游戏这些游戏对硬件要求不高但因为没有移动版一直没机会在手机上玩。通过FEXWineDXMT的方案这些老游戏可以在iOS上流畅运行。我试过几个2005年前后的RPG和策略游戏帧率稳定在60帧体验很好。6.2 生产力工具的兼容不只是游戏一些Windows平台的专业工具比如老版本的Photoshop、特定的EDA软件也可以通过这套方案在iOS上运行。当然触屏操作这些工具会比较别扭但配合外接键鼠应急使用是没问题的。6.3 跨平台开发的测试环境对于开发者来说这套方案可以作为一个轻量的Windows兼容性测试环境。不需要开虚拟机直接在iOS设备上跑Windows程序验证兼容性。当然这个场景比较小众但对特定需求的开发者来说很有价值。7. 我对Madeira这类项目的看法折腾了这么久我的核心体会是兼容层技术的价值不在于完美而在于可用。你不可能指望翻译后的性能跟原生一样也不可能指望所有Windows程序都能跑起来。但只要有一部分程序能跑而且体验可接受这个方案就有存在的意义。Madeira这个项目从关键词来看走的是FEX-EmuWineDXMT这条技术路线。这条路线的优点是组件成熟、社区活跃缺点是集成复杂度高、调试难度大。如果项目能把集成工作做好提供一套开箱即用的方案那对普通用户来说价值很大。我个人的建议是如果你对这个方向感兴趣先从桌面Linux上的Wine开始玩起熟悉Wine的配置和调试方法。然后再接触FEX-Emu理解动态二进制翻译的原理。最后再尝试iOS上的集成方案。这个学习路径比较平滑不会一上来就被各种报错劝退。另外参与这类开源项目的时候遇到问题先查issue和文档不要急着提新issue。很多问题别人已经遇到过了解决方案就在历史记录里。如果确实要提issue把日志、复现步骤、系统版本这些信息准备齐全这样开发者才能高效地帮你定位问题。最后说一个我自己的小技巧调试这类兼容层问题的时候准备一台Android设备作为对照。同样的游戏在Android上跑通了再到iOS上跑如果iOS上出问题就能确定是iOS特有的问题比如JIT权限、Metal适配而不是Wine或者FEX本身的bug。这个对照法能省很多排查时间。