2015年我們宣布計(jì)劃將Unity WebGL構(gòu)建目標(biāo)的導(dǎo)出文件改為WebAssembly,,即Wasm。現(xiàn)在Unity 2018.2已經(jīng)支持這項(xiàng)改動(dòng),。本篇文章我們將介紹它是如何實(shí)現(xiàn),,以及對(duì)使用Unity制作交互式Web內(nèi)容的開(kāi)發(fā)者的意義所在。
Wasm的發(fā)展
我們?cè)?a class="insidelink" href="http://shcjtec.com/Item/69424.aspx" target="_blank" title="Unity">Unity 5.6中曾對(duì)WebAssembly支持作為實(shí)驗(yàn)性功能發(fā)布,,WebAssembly剛開(kāi)始就得到四大主流桌面端瀏覽器的支持,。從此時(shí)起,Unity和瀏覽器就開(kāi)始加入不少相關(guān)的改進(jìn)內(nèi)容和錯(cuò)誤修復(fù),。
從此WebAssembly的用戶使用量也在增長(zhǎng),,我們得到了許多積極的反饋。所以下一步計(jì)劃是正式支持它:Unity 2018.1標(biāo)志著對(duì)WebAssembly支持的實(shí)驗(yàn)階段結(jié)束,,同時(shí)我們也實(shí)現(xiàn)了制作Wasm-only版本的可能,。
在Unity 2018.2,Wasm最終取代了asm.js,,成為了默認(rèn)鏈接目標(biāo)(Linker Target),。這意味著,,當(dāng)開(kāi)發(fā)者發(fā)布Unity WebGL構(gòu)建目標(biāo)時(shí),,Unity 2018 LTS將默認(rèn)使用Wasm。
這是一個(gè)非常重要的里程碑,,我們一直在為實(shí)現(xiàn)這個(gè)目標(biāo)而努力,。我們的要求是:確保Unity的實(shí)現(xiàn)和瀏覽器支持是穩(wěn)定的,并且需要覆蓋Wasm的內(nèi)部測(cè)試,,這其中包括升級(jí)Emscripten和修復(fù)代碼中的一些問(wèn)題,。
如今我們擁有了所有測(cè)試套件的Wasm變體,任何將要合并到程序主線的改動(dòng)都會(huì)針對(duì)WebAssembly進(jìn)行測(cè)試,。
請(qǐng)注意:我們?nèi)匀痪S護(hù)和運(yùn)行asm.js套件,,但現(xiàn)在所有要合并到主線的改動(dòng)都是針對(duì)Wasm進(jìn)行測(cè)試。
WebAssembly與asm.js
下面詳細(xì)介紹一下WebAssembly,,并探討與asm.js之間的區(qū)別,。
Wasm比asm.js速度更快,、容量更小且內(nèi)存使用率更高,這些都是Unity WebGL導(dǎo)出的痛點(diǎn),。Wasm也許不能解決所有的問(wèn)題,,但它能改善所有領(lǐng)域的平臺(tái)。需要注意,,性能可能會(huì)因?yàn)g覽器實(shí)現(xiàn)而異,。但所有瀏覽器廠商都將致力于支持和改進(jìn)WebAssembly。
打開(kāi)Wasm文件時(shí),,你會(huì)立即注意到它是一個(gè)二進(jìn)制文件,,而asm.js是個(gè)文本文件。這種發(fā)布代碼的方法比較緊湊,,但是也造成開(kāi)發(fā)者無(wú)法在調(diào)試的時(shí)候而閱讀或修改代碼,。
Wasm有自己的指令集,而asm.js則是“高度可優(yōu)化”的Javascript子集,。在開(kāi)發(fā)構(gòu)建中,,WebAssembly給算術(shù)運(yùn)算加入了更為精確的錯(cuò)誤檢測(cè)功能,它能在特定情況拋出異常,,例如:除以零,、將大浮點(diǎn)數(shù)舍為整數(shù)等。在非開(kāi)發(fā)版本中,,這類(lèi)算術(shù)錯(cuò)誤的檢測(cè)將被屏蔽,,所以不會(huì)影響用戶體驗(yàn)。
1,、代碼大小
為了生成WebAssembly,,我們有一套基于IL2CPP、emscripten和binaryen復(fù)雜的工具鏈,,它會(huì)將C/C++和C#代碼轉(zhuǎn)換為WebAssembly,。這會(huì)產(chǎn)生二進(jìn)制文件<build name>.wasm.code.Unityweb,從而實(shí)現(xiàn)比asm.js更小的版本,。
此外,,WebAssembly開(kāi)發(fā)版本的代碼大小要比asm.js小數(shù)十MB,而非開(kāi)發(fā)版本的大小會(huì)小幾百KB,。為了讓你了解衡量基線,,WebAssembly空項(xiàng)目的代碼大小約比asm.js的小12%,如果包含3D物理則會(huì)小18%,。
請(qǐng)注意:上圖的測(cè)試結(jié)果在測(cè)量時(shí)不包含任何非必要資源包,,也不包含內(nèi)置著色器,使用Brotli作為壓縮格式,。對(duì)于任意改進(jìn)內(nèi)容,,例如:性能,、內(nèi)存、載入時(shí)間,,得到的改進(jìn)效果會(huì)根據(jù)項(xiàng)目而異,。
2、內(nèi)存
限定Unity堆的大小是asm.js的限制之一,,即Unity堆的大小必須在構(gòu)建時(shí)指定,,后面無(wú)法進(jìn)行更改。WebAssembly能讓Unity堆的大小在運(yùn)行時(shí)增長(zhǎng),,從而使Unity內(nèi)容的內(nèi)存使用超過(guò)啟動(dòng)時(shí)指定的初始堆大小,。
這意味著開(kāi)發(fā)者可以讓內(nèi)容從例如32MB這樣的小型堆開(kāi)始,并讓它根據(jù)需要增長(zhǎng),,這在之前是無(wú)法實(shí)現(xiàn)的,。
將Memory Size值視為內(nèi)容一開(kāi)始的初始大小,這是Unity 2018.2中的功能,,所以可以立即使用此功能,。但是如果構(gòu)建目標(biāo)是asm.js,則無(wú)法使用此方法,,因?yàn)闊o(wú)法調(diào)整堆的大小,。
如果堆增長(zhǎng)得過(guò)多,瀏覽器可能會(huì)耗盡內(nèi)存,,是否過(guò)多取決于瀏覽器,。為了使各瀏覽器獲得一致的行為,請(qǐng)?jiān)O(shè)置Unity堆的最大值,。你可以通過(guò)在編輯器腳本中設(shè)置emscripten參數(shù) “-s WASM_MEM_MAX=<value>” 來(lái)實(shí)現(xiàn),。
例如:PlayerSettings.WebGL.emscriptenArgs = "-s WASM_MEM_MAX=512MB";
請(qǐng)注意:內(nèi)存最大值為2032,任何大于此數(shù)值都將導(dǎo)致瀏覽器中的運(yùn)行時(shí)錯(cuò)誤,。
Wasm在載入時(shí)能更高效地使用內(nèi)存,。因此它能減少用戶在使用asm.js時(shí)遇到的內(nèi)存不足問(wèn)題,尤其是在32位瀏覽器上,。更多關(guān)于Unity WebGL中的內(nèi)存處理方式,,請(qǐng)閱讀:Unity WebGL內(nèi)存詳解,。
3,、性能
Wasm和asm.js之間的性能差異取決于瀏覽器。Wasm作為二進(jìn)制文件擁有比解析為JavaScript文本文件的asm.js更快的載入速度,。已經(jīng)被編譯的Wasm代碼模塊會(huì)存儲(chǔ)在IndexedDB緩存中,,從而在重載相同內(nèi)容時(shí)實(shí)現(xiàn)快速的啟動(dòng)過(guò)程。為了利用Wasm緩存功能,,只要確保啟用Data Caching選項(xiàng)即可,。
啟動(dòng)后,,在針對(duì)asm.js代碼樣式優(yōu)化其JavaScript引擎的瀏覽器中,Assembly的執(zhí)行速度將和asm.js旗鼓相當(dāng),。如果在之前無(wú)法識(shí)別asm.js的瀏覽器上運(yùn)行Wasm,,那么Wasm的速度明顯會(huì)更快。
根據(jù)代碼的不同,,一些指令在Wasm的速度會(huì)更快,,例如:64位整數(shù)算術(shù),它在asm.js沒(méi)有相應(yīng)的特定指令,。
4,、多線程
WebAssembly多線程支持或許是最受期待的功能,也是最能提高性能的功能,。該功能本來(lái)應(yīng)該于今年早些時(shí)候在瀏覽器中推出,,但是由于Spectre和Meltdown的安全問(wèn)題,必須禁用SharedArrayBuffer支持,,而這是實(shí)現(xiàn)該功能的重要組成部分,。
好消息是,在最近多個(gè)瀏覽器加入了很多安全措施,,以便重新啟用SharedArrayBuffer,,我們也已經(jīng)看到多個(gè)跡象表明這些瀏覽器將要在即將推出的版本中發(fā)布該功能。
在Unity方面,,我們打算為該功能的發(fā)布做好準(zhǔn)備,,所以我們正積極地開(kāi)發(fā)Wasm多線程支持,它將在接下來(lái)數(shù)月作為實(shí)驗(yàn)功能發(fā)布,,該支持僅限于本地內(nèi)部線程,,例如:目前沒(méi)有C#線程。這里的內(nèi)部指的是用于蒙皮,,動(dòng)畫(huà),,剔除,AI尋路和其它子系統(tǒng)的作業(yè)線程,。一開(kāi)始它們可能不會(huì)被全部啟用,,但我們的長(zhǎng)期目標(biāo)是盡可能利用多線程功能。
調(diào)試
調(diào)試對(duì)asm.js來(lái)說(shuō)一直是個(gè)挑戰(zhàn),。但是WebAssembly中的調(diào)試還沒(méi)有變得更好,。雖然瀏覽器開(kāi)始在開(kāi)發(fā)套件中提供WebAssembly調(diào)試,但是這些調(diào)試工具尚未很好地?cái)U(kuò)展到Unity3D的內(nèi)容大小,。
Wasm的設(shè)計(jì)初衷是“開(kāi)放和可調(diào)試”,,所以可以期待瀏覽器在未來(lái)將提供實(shí)現(xiàn)該目標(biāo)的更好的工具。與此同時(shí),,開(kāi)發(fā)者可以使用其它調(diào)試方法:
通常Unity WebGL版本中的問(wèn)題出現(xiàn)在已構(gòu)建游戲與瀏覽器API交互層中,。該交互層位于UnityLoader.js和<build name>.asm/wasm.framework.Unityweb中,,它包含易于讀取的JavaScript代碼,便于通過(guò)瀏覽器內(nèi)置開(kāi)發(fā)工具進(jìn)行調(diào)試,。
對(duì)于調(diào)試C#代碼,,Debug.Log()通常是唯一選項(xiàng),因此建議盡可能在其它平臺(tái)上進(jìn)行調(diào)試,。
對(duì)于高級(jí)調(diào)試功能,,請(qǐng)嘗試導(dǎo)出為asm.js,從而能夠使用console.log().來(lái)注釋所生成的asm.js內(nèi)容,。
值得一提的是,,Unity 2018.2中加入了IL2CPP的托管代碼調(diào)試支持,在實(shí)現(xiàn)WebAssembly多線程支持后,,我們會(huì)立即開(kāi)始做調(diào)試實(shí)驗(yàn),。
未來(lái)展望
各瀏覽器廠商會(huì)致力繼續(xù)改進(jìn)WebAssembly支持,他們一直在開(kāi)發(fā)新功能和優(yōu)化內(nèi)容,,用于改進(jìn)啟動(dòng)時(shí)間和性能,。
例如:
?異步Wasm實(shí)例化功能(在Unity中得到支持)
?Wasm結(jié)構(gòu)化克隆功能,允許編譯后的Wasm在瀏覽器中緩存(在Unity中得到支持)
?基線和分層編譯,,以加速實(shí)例化過(guò)程(在運(yùn)行Unity內(nèi)容時(shí)會(huì)自動(dòng)支持)
?流式實(shí)例化功能,,能夠?qū)崿F(xiàn)在下載時(shí)編譯Wasm代碼(正考慮是否在Unity加入支持)
?多線程功能(正在開(kāi)發(fā)該功能在Unity中的支持)
?上述的一些功能已經(jīng)實(shí)現(xiàn),具體取決于瀏覽器,。
我們堅(jiān)信WebAssembly的未來(lái),,也鼓勵(lì)開(kāi)發(fā)人員默認(rèn)使用它來(lái)進(jìn)行開(kāi)發(fā)。如有需要,,可以將asm.js保留為舊版瀏覽器的運(yùn)行時(shí)回退,。該功能可以通過(guò)在WebGL Player Settings中選取WebGLLinkerTarget.Both實(shí)現(xiàn)。
我們計(jì)劃在Unity 2018.3中棄用asm.js,。這意味著在將來(lái),,asm.js將不會(huì)獲得任何針對(duì)Wasm的改進(jìn)內(nèi)容,例如:多線程功能,、SIMD等,。但asm.js還會(huì)在Unity 2018 LTS發(fā)布后的二年時(shí)間里面得到支持。