行Windows程序:Wine+FEX-Emu+DXMT四層翻譯棧實(shí)戰(zhàn))
1. 項(xiàng)目緣起為什么要在 iOS 上折騰 Wine第一次聽到“Madeira”這個(gè)名字很多人會(huì)以為是那個(gè)葡萄牙的旅游海島但在我們這群喜歡在移動(dòng)設(shè)備上折騰桌面應(yīng)用的人眼里它指向的是另一件事——把 Wine 這套 Windows 兼容層想辦法搬到 iOS 設(shè)備上去跑。這個(gè)想法聽起來有點(diǎn)瘋狂畢竟 iOS 的沙盒機(jī)制、簽名限制、沒有真正的本地可執(zhí)行權(quán)限每一條都在跟你作對(duì)。但偏偏就是有人不信邪從 FEX-Emu 到 DXMT從 x86-64 指令翻譯到 Metal 圖形轉(zhuǎn)譯硬生生拼出了一條路。我做這個(gè)方向的探索大概有兩年多時(shí)間中間踩過的坑比走通的路多得多。最開始是在桌面 Linux 上用 Wine 跑一些老舊的 Windows 工具后來接觸到 FEX-Emu 這個(gè)項(xiàng)目發(fā)現(xiàn)它能在 ARM 設(shè)備上翻譯 x86-64 指令再后來看到有人把 Wine、FEX-Emu、DXMT 這幾樣?xùn)|西串起來在 iOS 上跑起了 Windows 程序當(dāng)時(shí)第一反應(yīng)是“這也能行”第二反應(yīng)就是“我得自己試試”。這篇文章不是一篇官方文檔式的教程而是我把自己從零開始搭建、調(diào)試、踩坑、再調(diào)試的整個(gè)過程整理出來。適合哪些人看如果你是對(duì) iOS 底層機(jī)制好奇的開發(fā)者如果你手上有越獄設(shè)備或者自簽環(huán)境想跑點(diǎn) Windows 老程序如果你在做跨平臺(tái)兼容層的研究那這篇內(nèi)容應(yīng)該能幫你省下不少試錯(cuò)時(shí)間。如果你只是普通用戶想裝個(gè) Windows 軟件玩玩我建議你先看完再?zèng)Q定要不要入坑因?yàn)檫@條路確實(shí)不輕松。核心關(guān)鍵詞就幾個(gè)Wine、FEX-Emu、DXMT、iOS、x86-64。把這五個(gè)詞之間的關(guān)系理清楚整個(gè)項(xiàng)目的脈絡(luò)就清楚了。Wine 負(fù)責(zé)提供 Windows API 的兼容實(shí)現(xiàn)FEX-Emu 負(fù)責(zé)把 x86-64 指令翻譯成 ARM64 能執(zhí)行的指令DXMT 負(fù)責(zé)把 Direct3D 調(diào)用轉(zhuǎn)譯成 MetaliOS 是運(yùn)行平臺(tái)x86-64 是目標(biāo)程序的指令集架構(gòu)。這四層疊在一起才讓一個(gè) Windows 程序在 iPhone 或 iPad 上跑起來成為可能。2. 整體架構(gòu)拆解四層翻譯棧是怎么疊起來的2.1 從 Windows 程序到 iOS 屏幕的完整鏈路先把這個(gè)鏈路講清楚不然后面操作的時(shí)候你都不知道自己在干什么。一個(gè) Windows 程序在 iOS 上運(yùn)行大致要經(jīng)過這么幾個(gè)階段第一層PE 加載。Wine 的 loader 讀取 Windows 的 PE 格式可執(zhí)行文件解析導(dǎo)入表、重定位表把程序加載到內(nèi)存里。這一步跟你在 Linux 上用 Wine 是一樣的。第二層指令翻譯。程序里的 x86-64 指令不能直接在 ARM64 的 iOS 設(shè)備上跑FEX-Emu 這時(shí)候介入把 x86-64 指令塊翻譯成 ARM64 指令塊并且做緩存。第一次執(zhí)行某段代碼會(huì)慢后面命中緩存就快了。第三層API 轉(zhuǎn)換。Windows 程序調(diào)用 kernel32.dll、user32.dll 這些系統(tǒng)庫(kù)的時(shí)候Wine 把這些調(diào)用轉(zhuǎn)換成 POSIX 調(diào)用或者 iOS 能理解的接口。圖形方面如果程序用 Direct3DDXMT 會(huì)把 D3D 調(diào)用轉(zhuǎn)成 Metal 調(diào)用。第四層圖形輸出。最終畫面通過 Metal 渲染到 iOS 的屏幕上輸入事件從 iOS 的觸摸或外接鍵鼠反向傳遞回 Wine 的消息循環(huán)。這四層每一層都有性能損耗疊在一起能跑起來就已經(jīng)是奇跡了。我實(shí)測(cè)下來一個(gè)簡(jiǎn)單的 Win32 記事本程序在 A15 芯片的 iPhone 上啟動(dòng)大概要三到五秒復(fù)雜一點(diǎn)的程序可能要十幾秒甚至更久。所以如果你打算跑大型游戲或者專業(yè)軟件期望值要放低一點(diǎn)。2.2 為什么選 FEX-Emu 而不是其他方案在 ARM 設(shè)備上跑 x86 程序市面上有幾個(gè)選擇QEMU 做全系統(tǒng)模擬、Box64 做用戶態(tài)翻譯、FEX-Emu 也是用戶態(tài)翻譯。我選 FEX-Emu 的原因有幾個(gè)第一FEX-Emu 對(duì) x86-64 指令集的覆蓋比較完整尤其是 SSE4.2、AVX 這些擴(kuò)展指令的支持比 Box64 要早。很多 Windows 程序編譯的時(shí)候默認(rèn)開了 SSE4.2如果翻譯層不支持直接就是非法指令崩潰。第二FEX-Emu 的 JIT 緩存機(jī)制比較成熟。它會(huì)把翻譯過的指令塊緩存起來下次執(zhí)行同樣的代碼直接走緩存不用重新翻譯。這在 iOS 這種內(nèi)存受限的環(huán)境下特別重要因?yàn)槟憧梢钥刂凭彺娲笮”苊獗幌到y(tǒng)殺掉。第三FEX-Emu 的社區(qū)活躍度不錯(cuò)遇到問題去翻 issue 和討論區(qū)大概率能找到答案或者至少找到方向。Box64 雖然也不錯(cuò)但在 iOS 上的實(shí)踐案例相對(duì)少一些。當(dāng)然 FEX-Emu 也不是沒有缺點(diǎn)。它的配置相對(duì)復(fù)雜需要調(diào)不少參數(shù)而且對(duì) 32 位 x86 程序的支持不如 64 位那么好。如果你要跑的是老舊的 32 位程序可能還得額外折騰。2.3 DXMT 的角色為什么不用 DXVK 或 WineD3D圖形轉(zhuǎn)譯這塊桌面 Linux 上大家常用 DXVK把 D3D 轉(zhuǎn) Vulkan或者 WineD3D把 D3D 轉(zhuǎn) OpenGL。但在 iOS 上Vulkan 沒有原生支持OpenGL 也被蘋果標(biāo)記為廢棄了唯一可用的現(xiàn)代圖形 API 就是 Metal。DXMT 就是專門做 D3D 到 Metal 轉(zhuǎn)譯的項(xiàng)目。我試過用 WineD3D 跑一些老程序在 iOS 上通過 OpenGL ES 轉(zhuǎn)譯性能慘不忍睹而且兼容性問題很多。DXMT 雖然還比較年輕但對(duì) D3D11 的支持已經(jīng)能跑不少程序了。D3D9 的支持也在完善中D3D12 目前基本不用想。這里有個(gè)細(xì)節(jié)要注意DXMT 需要 Metal 的一些高級(jí)特性比如 argument buffer、indirect command buffer 這些。老設(shè)備上這些特性可能不支持或者支持不完整所以 DXMT 在 iPhone X 及以后的設(shè)備上表現(xiàn)會(huì)好很多。如果你用的是更老的設(shè)備可能只能退回 WineD3D 方案性能就別指望了。3. 環(huán)境準(zhǔn)備在 iOS 上搭建運(yùn)行環(huán)境的前置條件3.1 設(shè)備與系統(tǒng)版本的選擇不是所有 iOS 設(shè)備都能跑這套東西。根據(jù)我的實(shí)測(cè)經(jīng)驗(yàn)有幾個(gè)硬性條件芯片A12 及以上。A11 及更早的芯片在 Metal 特性支持和內(nèi)存帶寬上都吃緊跑起來體驗(yàn)很差。內(nèi)存建議 4GB 及以上。Wine 本身加上 FEX-Emu 的翻譯緩存再加上目標(biāo)程序2GB 內(nèi)存的設(shè)備基本一啟動(dòng)就閃退。系統(tǒng)版本iOS 15 到 iOS 17 之間的版本兼容性最好。太新的系統(tǒng)對(duì) JIT 和內(nèi)存管理的限制更嚴(yán)太老的系統(tǒng)缺少一些必要的 API。存儲(chǔ)空間至少預(yù)留 10GB。Wine 的 prefix 目錄、FEX-Emu 的緩存、目標(biāo)程序本身加起來很容易超過 5GB。我手頭測(cè)試用的是一臺(tái) iPhone 13 ProA156GB 內(nèi)存和一臺(tái) iPad Pro 11 寸M18GB 內(nèi)存。M1 的 iPad 體驗(yàn)明顯好很多因?yàn)閮?nèi)存大、散熱好長(zhǎng)時(shí)間跑不容易降頻。3.2 簽名與權(quán)限最容易被卡住的一步iOS 的沙盒機(jī)制決定了你不能隨便運(yùn)行可執(zhí)行文件。要讓 Wine 和 FEX-Emu 跑起來你需要解決幾個(gè)權(quán)限問題JIT 權(quán)限。FEX-Emu 需要?jiǎng)討B(tài)生成 ARM64 代碼并執(zhí)行這需要 JIT 權(quán)限。在 iOS 上只有通過特定的調(diào)試方式或者越獄環(huán)境才能獲得 JIT 權(quán)限。如果你用的是自簽證書需要開啟get-task-allow權(quán)限并且通過調(diào)試器附加的方式啟動(dòng)應(yīng)用??蓤?zhí)行內(nèi)存。iOS 默認(rèn)不允許同時(shí)可寫和可執(zhí)行的內(nèi)存頁W^X 保護(hù)。FEX-Emu 的 JIT 緩存需要可執(zhí)行內(nèi)存所以要么越獄后關(guān)閉這個(gè)保護(hù)要么使用特定的 API 來申請(qǐng)可執(zhí)行內(nèi)存。文件系統(tǒng)訪問。Wine 需要?jiǎng)?chuàng)建和管理 prefix 目錄里面會(huì)有大量的文件讀寫操作。在沙盒環(huán)境下你只能在自己的應(yīng)用容器內(nèi)操作不能訪問其他應(yīng)用的數(shù)據(jù)。我自己的做法是在一臺(tái)越獄的 iPad 上做主要測(cè)試因?yàn)樵姜z環(huán)境省去了很多簽名和權(quán)限的麻煩。但如果你不想越獄也可以走自簽加調(diào)試器附加的路線只是每次啟動(dòng)都要連電腦比較麻煩。注意越獄和自簽都涉及設(shè)備安全策略的調(diào)整操作前請(qǐng)確認(rèn)你了解相關(guān)風(fēng)險(xiǎn)并且只在你自己擁有的設(shè)備上進(jìn)行測(cè)試。3.3 必備組件清單與獲取方式搭建這套環(huán)境需要以下幾個(gè)核心組件組件作用獲取方式WineWindows API 兼容層從官方源碼編譯 ARM64 版本FEX-Emux86-64 到 ARM64 指令翻譯從官方倉(cāng)庫(kù)編譯 iOS 版本DXMTD3D 到 Metal 轉(zhuǎn)譯從項(xiàng)目倉(cāng)庫(kù)編譯MoltenVKVulkan 到 Metal 轉(zhuǎn)譯可選從官方倉(cāng)庫(kù)獲取依賴庫(kù)各種運(yùn)行時(shí)庫(kù)通過包管理器或手動(dòng)編譯編譯這些組件是個(gè)大工程尤其是 Wine 和 FEX-Emu在 macOS 上交叉編譯到 iOS 需要配置不少工具鏈。如果你不想自己編譯也可以找社區(qū)里已經(jīng)編譯好的版本但要注意版本匹配問題。Wine 的版本和 FEX-Emu 的版本如果不匹配可能會(huì)出現(xiàn)各種奇怪的崩潰。我建議的版本組合是Wine 8.x 系列 FEX-Emu 最新穩(wěn)定版 DXMT 最新版。這個(gè)組合我實(shí)測(cè)下來穩(wěn)定性最好。Wine 9.x 雖然新但和 FEX-Emu 的配合還有些問題容易出現(xiàn)翻譯錯(cuò)誤。4. 實(shí)操過程從零開始搭建運(yùn)行環(huán)境4.1 編譯 Wine 的 ARM64 版本這一步是整個(gè)流程里最耗時(shí)的。Wine 的代碼量很大交叉編譯到 iOS 需要不少耐心。首先你需要準(zhǔn)備一臺(tái) macOS 電腦安裝 Xcode 和 Command Line Tools。然后獲取 iOS 的 SDK配置交叉編譯工具鏈。Wine 官方?jīng)]有提供 iOS 的編譯腳本你需要自己寫一個(gè) configure 的參數(shù)配置。關(guān)鍵的 configure 參數(shù)包括./configure \ --hostarm-apple-darwin \ --with-wine-tools../wine-tools \ --disable-tests \ --without-x \ --without-freetype \ --without-alsa \ --without-pulse \ --without-cups \ --without-dbus \ --without-gnutls \ --without-krb5 \ --without-netapi \ --without-opencl \ --without-osmesa \ --without-oss \ --without-pcap \ --without-sane \ --without-udev \ --without-usb \ --without-v4l2 \ --without-vulkan \ --without-wayland \ --without-xcomposite \ --without-xcursor \ --without-xfixes \ --without-xinerama \ --without-xinput \ --without-xinput2 \ --without-xrandr \ --without-xrender \ --without-xshape \ --without-xshm \ --without-xinerama這些--without參數(shù)是為了去掉 iOS 上不需要或者不支持的依賴。編譯過程中可能會(huì)遇到各種頭文件缺失的問題需要手動(dòng)補(bǔ)一些 stub 頭文件。編譯完成后你會(huì)得到wine、wineserver、wineboot等可執(zhí)行文件以及一堆.dll和.so文件。這些文件需要打包到 iOS 應(yīng)用的 bundle 里。實(shí)操心得編譯 Wine 的時(shí)候建議用make -j$(sysctl -n hw.ncpu)并行編譯能省不少時(shí)間。但注意內(nèi)存占用如果 Mac 內(nèi)存不夠并行數(shù)調(diào)低一點(diǎn)不然會(huì)頻繁 swap。4.2 編譯 FEX-Emu 并配置 JITFEX-Emu 的編譯相對(duì) Wine 要簡(jiǎn)單一些但 JIT 配置是個(gè)關(guān)鍵點(diǎn)。FEX-Emu 的源碼里有一個(gè)Source/Tools/FEXLoader目錄這是加載器的入口。編譯的時(shí)候需要開啟ENABLE_JIT選項(xiàng)并且配置好 JIT 緩存的大小。在 iOS 上我建議把 JIT 緩存限制在 256MB 到 512MB 之間太大了容易被系統(tǒng)殺掉太小了又頻繁觸發(fā)重新翻譯。FEX-Emu 的配置文件Config.json里有幾個(gè)關(guān)鍵參數(shù){ JITCacheSize: 268435456, JITInlineCacheSize: 16777216, TSOEnabled: true, SMCChecks: mtrack, X87ReducedPrecision: true, Multiblock: true, DynamicL1Cache: true }TSOEnabled是開啟 x86 的內(nèi)存序模型模擬這個(gè)必須開不然多線程程序會(huì)出各種數(shù)據(jù)競(jìng)爭(zhēng)問題。SMCChecks是自修改代碼檢測(cè)有些程序會(huì)動(dòng)態(tài)修改自己的代碼不開這個(gè)會(huì)跑飛。Multiblock是開啟多塊編譯能提升性能但會(huì)增加編譯時(shí)間。JIT 權(quán)限的獲取是另一個(gè)難點(diǎn)。在越獄環(huán)境下你可以直接調(diào)用mprotect把內(nèi)存頁設(shè)成可執(zhí)行。在非越獄環(huán)境下你需要通過pthread_jit_write_protect_np這個(gè) API 來切換內(nèi)存頁的寫和執(zhí)行權(quán)限。這個(gè) API 在 iOS 14 以后才可用而且需要特定的 entitlement。4.3 集成 DXMT 并配置 Metal 后端DXMT 的編譯需要 Metal 的 SDK在 macOS 上編譯到 iOS 需要配置 Metal 的交叉編譯工具鏈。DXMT 的核心是把 D3D 的著色器字節(jié)碼轉(zhuǎn)譯成 Metal 的著色器語言。它內(nèi)部有一個(gè) D3D 字節(jié)碼解析器和一個(gè) Metal 代碼生成器。編譯的時(shí)候需要鏈接 Metal 框架和 Foundation 框架。配置 DXMT 的時(shí)候有幾個(gè)關(guān)鍵點(diǎn)Metal 設(shè)備選擇在 iOS 上只有一個(gè) Metal 設(shè)備直接調(diào)用MTLCreateSystemDefaultDevice()就行。命令隊(duì)列DXMT 會(huì)創(chuàng)建一個(gè)或多個(gè) Metal 命令隊(duì)列建議根據(jù) CPU 核心數(shù)來配置一般 2 到 4 個(gè)隊(duì)列比較合適。紋理格式映射D3D 的紋理格式和 Metal 的紋理格式不是一一對(duì)應(yīng)的DXMT 內(nèi)部做了映射表但有些格式可能不支持需要 fallback 到軟件渲染。我實(shí)測(cè)下來DXMT 對(duì) D3D11 的支持已經(jīng)能跑不少程序了比如一些老版本的 3D 建模軟件、簡(jiǎn)單的游戲。但 D3D9 的支持還有些問題部分程序會(huì)出現(xiàn)畫面閃爍或者紋理錯(cuò)亂。4.4 打包成 iOS 應(yīng)用并簽名把 Wine、FEX-Emu、DXMT 和你的目標(biāo)程序打包成一個(gè) iOS 應(yīng)用需要?jiǎng)?chuàng)建一個(gè) Xcode 工程把這些組件作為資源文件嵌入然后寫一個(gè)啟動(dòng)器來調(diào)用它們。啟動(dòng)器的核心邏輯是初始化 FEX-Emu 的 JIT 環(huán)境。設(shè)置 Wine 的環(huán)境變量比如WINEPREFIX、WINEDLLPATH等。調(diào)用wine的入口函數(shù)傳入目標(biāo)程序的路徑。處理 iOS 的生命周期事件比如進(jìn)入后臺(tái)時(shí)暫停 Wine 的消息循環(huán)。簽名的時(shí)候需要注意如果你的應(yīng)用需要 JIT 權(quán)限需要在 entitlements 文件里加上com.apple.security.cs.allow-jit和com.apple.security.cs.allow-unsigned-executable-memory。這兩個(gè) entitlement 在自簽環(huán)境下需要特殊的配置才能生效。注意自簽應(yīng)用的證書有效期通常只有 7 天過期后需要重新簽名。如果你要長(zhǎng)期使用建議考慮開發(fā)者賬號(hào)或者越獄環(huán)境。5. 常見問題與排查技巧實(shí)錄5.1 Wine 亂碼問題從根源到解決Wine 亂碼是我遇到最多的問題沒有之一。表現(xiàn)就是程序界面上的中文顯示成方塊、問號(hào)或者一堆亂碼字符。這個(gè)問題的根源是字體和字符集配置。Wine 默認(rèn)使用它自帶的字體但這些字體對(duì)中文的支持不完整。解決方法有幾個(gè)方法一安裝中文字體到 Wine 的字體目錄。把simsun.ttc、msyh.ttf這些中文字體復(fù)制到$WINEPREFIX/drive_c/windows/Fonts/目錄下然后在注冊(cè)表里配置字體替換。方法二修改注冊(cè)表的字體鏈接。在HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink里添加字體映射把Tahoma、MS Sans Serif這些默認(rèn)字體鏈接到中文字體。方法三設(shè)置LANG和LC_ALL環(huán)境變量。在啟動(dòng) Wine 之前設(shè)置LANGzh_CN.UTF-8讓 Wine 知道你要用中文環(huán)境。我自己的做法是方法一加方法二組合使用先把字體文件放進(jìn)去再改注冊(cè)表。這樣大部分程序的中文顯示就正常了。但有些程序用的是自己的字體渲染引擎可能還是會(huì)有問題那就只能針對(duì)具體程序來調(diào)。還有一個(gè)坑是wine 欄是亂碼這個(gè)通常是指 Wine 的窗口標(biāo)題欄亂碼。這個(gè)問題的原因是 Wine 的窗口管理器沒有正確加載字體。解決方法是在WINEPREFIX的user.reg文件里找到[Software\\Wine\\Fonts]段把Replacements的值改成你安裝的中文字體。5.2 FEX-Emu 崩潰與非法指令排查FEX-Emu 崩潰通常有幾個(gè)原因非法指令。表現(xiàn)是程序啟動(dòng)后立刻崩潰日志里會(huì)有SIGILL信號(hào)。這通常是因?yàn)?FEX-Emu 不支持某條 x86-64 指令。解決方法是更新 FEX-Emu 到最新版本或者在配置里開啟X87ReducedPrecision和AVXEnabled這些選項(xiàng)。內(nèi)存訪問錯(cuò)誤。表現(xiàn)是SIGSEGV信號(hào)程序隨機(jī)崩潰。這可能是 JIT 緩存太小翻譯代碼被換出后重新執(zhí)行時(shí)出錯(cuò)。解決方法是增大JITCacheSize或者關(guān)閉DynamicL1Cache。翻譯緩存沖突。表現(xiàn)是程序運(yùn)行一段時(shí)間后崩潰日志里有Translation cache full之類的信息。解決方法是清理 FEX-Emu 的緩存目錄或者增大緩存上限。我整理了一個(gè)排查速查表癥狀可能原因解決方法啟動(dòng)即崩潰SIGILL不支持的指令更新 FEX-Emu開啟兼容選項(xiàng)隨機(jī)崩潰SIGSEGVJIT 緩存不足增大 JITCacheSize運(yùn)行一段時(shí)間后崩潰翻譯緩存滿清理緩存或增大上限多線程程序數(shù)據(jù)錯(cuò)亂TSO 未開啟開啟 TSOEnabled自修改代碼程序崩潰SMC 檢測(cè)未開設(shè)置 SMCChecks 為 mtrack5.3 DXMT 圖形問題黑屏、閃爍、紋理錯(cuò)亂DXMT 的圖形問題也比較常見我遇到過的有黑屏。程序啟動(dòng)了有聲音但畫面全黑。這通常是 Metal 渲染管線創(chuàng)建失敗。檢查日志里有沒有Failed to create pipeline state之類的錯(cuò)誤。解決方法可能是目標(biāo)程序用了 DXMT 不支持的著色器特性或者 Metal 設(shè)備不支持某些紋理格式。畫面閃爍。這通常是幀同步問題。DXMT 默認(rèn)使用三重緩沖但在 iOS 上可能和系統(tǒng)的顯示刷新不同步。解決方法是調(diào)整 DXMT 的PresentInterval參數(shù)改成 1垂直同步或者 0立即呈現(xiàn)試試。紋理錯(cuò)亂。這通常是紋理格式映射錯(cuò)誤。D3D 的某些壓縮紋理格式在 Metal 上沒有直接對(duì)應(yīng)DXMT 會(huì)做轉(zhuǎn)換但轉(zhuǎn)換過程中可能出錯(cuò)。解決方法是開啟 DXMT 的TextureFormatFallback選項(xiàng)讓不支持的格式回退到軟件解碼。實(shí)操心得DXMT 的日志級(jí)別可以調(diào)到 debug這樣能看到詳細(xì)的圖形調(diào)用信息。雖然日志量很大但排查問題的時(shí)候非常有用。建議在Config.json里把LogLevel設(shè)成debug復(fù)現(xiàn)問題后再調(diào)回info。5.4 性能優(yōu)化讓程序跑得更流暢性能優(yōu)化是個(gè)持續(xù)的過程我總結(jié)了幾條經(jīng)驗(yàn)關(guān)閉不必要的 Wine 服務(wù)。Wine 默認(rèn)會(huì)啟動(dòng)一些后臺(tái)服務(wù)比如wineserver、plugplay、svchost等。在 iOS 上這些服務(wù)會(huì)占用寶貴的內(nèi)存和 CPU。可以在啟動(dòng)參數(shù)里加上WINEDLLOVERRIDES來禁用一些不需要的 DLL。調(diào)整 FEX-Emu 的編譯策略。Multiblock開啟后FEX-Emu 會(huì)把多個(gè)基本塊一起編譯減少翻譯次數(shù)但會(huì)增加單次編譯時(shí)間。對(duì)于交互式程序建議開啟對(duì)于批處理程序可以關(guān)閉。限制幀率。iOS 設(shè)備的屏幕刷新率是 60Hz 或 120Hz但 Windows 程序可能以更高的幀率渲染。在 DXMT 里設(shè)置幀率上限可以減少 GPU 負(fù)載和發(fā)熱。使用外接散熱。iPhone 跑這套東西發(fā)熱量很大一熱就降頻降頻就卡。我測(cè)試的時(shí)候用了一個(gè)半導(dǎo)體散熱背夾幀率穩(wěn)定性提升明顯。6. 擴(kuò)展玩法這套環(huán)境還能做什么6.1 跑老游戲和懷舊軟件這套環(huán)境最直接的用途就是跑那些 Windows 老游戲和老軟件。我測(cè)試過一些 2000 年代的單機(jī)游戲比如策略類、角色扮演類的大部分都能跑起來幀率在 20 到 30 之間對(duì)于這類游戲來說夠用了。一些老版本的辦公軟件、圖像處理軟件也能跑但體驗(yàn)一般主要是屏幕適配和輸入方式的問題。iOS 的觸摸操作和 Windows 的鼠標(biāo)操作差異很大需要外接藍(lán)牙鍵鼠才能正常使用。6.2 作為開發(fā)測(cè)試環(huán)境如果你在做 Windows 程序的跨平臺(tái)適配這套環(huán)境可以作為一個(gè)輕量級(jí)的測(cè)試平臺(tái)。不需要開虛擬機(jī)直接在 iOS 設(shè)備上就能驗(yàn)證程序在 ARM 架構(gòu)上的兼容性。我有時(shí)候會(huì)用它來測(cè)試一些自己寫的小工具編譯成 Windows 版本后直接在 iPad 上跑看看有沒有架構(gòu)相關(guān)的問題。雖然不能完全替代真實(shí)的 Windows 環(huán)境但作為一個(gè)快速驗(yàn)證的手段還是不錯(cuò)的。6.3 與 iOS 原生功能的結(jié)合這套環(huán)境跑起來之后理論上可以和 iOS 的原生功能做一些結(jié)合。比如把 Wine 程序的輸出通過 iOS 的分享接口導(dǎo)出或者用 iOS 的快捷指令來觸發(fā) Wine 程序的運(yùn)行。我試過用快捷指令啟動(dòng) Wine 程序然后自動(dòng)把生成的文件保存到 iCloud Drive。這個(gè)流程跑通之后用起來還挺方便的。但要注意Wine 程序的文件操作是在沙盒內(nèi)進(jìn)行的要訪問外部文件需要額外的配置。7. 個(gè)人經(jīng)驗(yàn)與后續(xù)折騰方向這套東西我斷斷續(xù)續(xù)折騰了兩年多中間有幾次差點(diǎn)放棄。最崩潰的一次是編譯了一整天的 Wine結(jié)果在 iOS 上啟動(dòng)就崩潰查了半天發(fā)現(xiàn)是一個(gè)依賴庫(kù)的版本不匹配。還有一次是 FEX-Emu 的 JIT 緩存配置不對(duì)程序跑幾分鐘就閃退排查了好幾天才找到原因。但每次跑通一個(gè)新程序的時(shí)候那種成就感還是很足的。尤其是看到一些十幾年前的 Windows 軟件在 iPhone 上流暢運(yùn)行會(huì)覺得這些折騰都值了。后續(xù)我打算繼續(xù)跟進(jìn) FEX-Emu 和 DXMT 的更新看看能不能支持更多的程序和更好的性能。另外也在研究怎么把 Vulkan 的支持加進(jìn)來通過 MoltenVK 讓一些用 Vulkan 的 Windows 程序也能跑。這條路還很長(zhǎng)但方向是清晰的。如果你也在折騰這個(gè)方向我的建議是先從簡(jiǎn)單的程序開始把整個(gè)流程跑通再逐步增加復(fù)雜度。不要一上來就挑戰(zhàn)大型游戲或者專業(yè)軟件那樣很容易受挫。遇到問題多查日志多翻社區(qū)討論大部分問題別人都遇到過。最后保持耐心這套東西的調(diào)試周期是以天為單位的急不來。