【JSConf EU 2018】Rust + WebAssembly

leyayun發表於2018-06-26

歐洲JSConf上的神祕專案

在今年的歐洲JSConf上來自Mozilla的Lin Clark為我們展示一個神祕專案,一個的拱形彩虹門(視訊傳送門),它實際上是由三萬個彩色LED組成的畫布,可以展現燈光動畫,並且通過Rust編寫的WebAssembly模組來控制“拱門”的燈光動畫。Lin在2017年的JSConf上也曾做過關於WebAssembly的演講,在該演講中她提到2008年是JavaScript執行效率曲線的一個拐點,隨著眾多瀏覽器加入了JIT編譯器(Just-in-time compiler),JavaScript的執行效能帶來了十倍的增速,這為JavaScript插上了一雙翅膀使它可以自由翱翔在瀏覽器端、伺服器端和客戶端。Lin指出WebAssembly的誕生可能會成為曲線的下一個拐點。可見Mozilla對WebAssembly的重視程度。

【JSConf EU 2018】Rust + WebAssembly

emm...讓我們回到2018年。Lin在會上說明了如何使用WebAssembly模組控制“拱門”的燈光動畫。下面讓我們來看下這是如何做到的。

用位元組把空間或時間連續起來

我們所處的空間是一個三維空間,如果再給它加上時間維度,那它就是四維空間。

【JSConf EU 2018】Rust + WebAssembly

計算機是沒有辦法理解這個四維空間的,如果要讓計算機可以“理解”,我們需要對四維空間降維。首先是時間維度,可以通過幀來完成的。螢幕就像一個翻頁的書,每一幀就相當於書的一頁。

【JSConf EU 2018】Rust + WebAssembly

在web端,60FPS是可以通過螢幕流暢展示動畫的要求。這意味著你有60個不同的螢幕快照--一秒時間內60個點的動畫定格的樣子。可以想象下一連串代表著三維空間的快照。

【JSConf EU 2018】Rust + WebAssembly

現在要從三維降到二維,我們要做的是將空間壓平到一個方格紙上。

【JSConf EU 2018】Rust + WebAssembly

現在已經下降到了二維,我們需要再一次降維。把方格紙上面的每一行拿出來按順序連線起來。

【JSConf EU 2018】Rust + WebAssembly

現在下降到了一行畫素,我們已經可以把它放到記憶體裡面了,因為WebAssembly記憶體(linear memory)基本就是一排小格子。這意味著我們已經下降到一維結構,但是仍然擁有著代表著二維、三維或四維的全部資料。只是現在它們是以一種連續的、線性的方式在展示。

線性記憶體(Linear memory)

線性記憶體是JavaScript和WebAssembly的一個主要通訊方式,WebAssembly和執行它的JavaScript都可以訪問這個物件。線性記憶體實際上是一個可變大小的ArrayBuffer物件,本質上是連續的、按位元組可定址的一段記憶體。

【JSConf EU 2018】Rust + WebAssembly

為了使上面提到的“拱門”產生燈光動畫,JavaScript告訴WebAssembly模組:“好的,現在就填入動畫。”,這一步通過JavaScript呼叫WebAssembly的function來完成。

【JSConf EU 2018】Rust + WebAssembly

通過WebAssembly為線性記憶體裡的每一個畫素填入顏色。

【JSConf EU 2018】Rust + WebAssembly

然後JavaScript程式碼將獲取這些顏色資料並把他們轉化成一個JSON陣列傳送給“拱門”。

【JSConf EU 2018】Rust + WebAssembly

接下來我們來看下如何在JavaScript裡使用這些資料。

為線性記憶體填入顏色

線性記憶體其實是一大行0和1。如果你想賦予這些0和1意義,那你需要指出如何分割它們。你要做的是為AarryBuffer建立一個TypedArray,告訴JavaScript如何對AarryBuffer裡的位元位(bit)行分段。就像正在繪製的格子圍繞著這些位元位對它們說那些位元位是屬於那些數字。

【JSConf EU 2018】Rust + WebAssembly

例如你用了一個16進位制的值,那你的數字將有24位的寬度。所以你需要一個24位的格子。每個格子都包含一個畫素,而可容納24位最小的格子是32位的(int32),所以我們將在這個緩衝區上建立一個Int32Array的檢視。它將這段緩衝區的位元組碼包裹到格子裡。在這個例子裡我們需要新增一些空白來填充這個格子由24位補齊到32位。

如果我們使用RGB值,這些格子將是8位的寬度。為了得到一個RGB的值,你將使用每三個格子代表你的R、G和B的值。這意味著你需要遍歷這些格子,並取出它們裡面的數字。

【JSConf EU 2018】Rust + WebAssembly

直接使用線性記憶體,你需要手動(寫一些程式碼)遍歷它,把它裡面的資料取出來存放到更加合理的資料結構裡。對於這個專案來說,這樣做不是很糟糕。顏色的對映是數字,它們相對容易使用線性記憶體來表示。我們使用的資料結構(RGB值)也不是很複雜。但是如果你使用的是更加複雜的資料結構,直接處理記憶體將會很痛苦。如果你可以直接傳一個JavaScript物件給WebAssembly,讓WebAssembly去維護它,這將會變簡單很多。你只需要新增一個很小的庫(wasm-bindgen)就可以做到這些。

使用wasm-bindgen

wasm-bindgen是使用Rust寫的一個庫,它可以使JavaScript與WebAssembly模組之間的資料互動變的更簡單。它用一個JavaScript wrapper 來包裹WebAssembly模組。這個wrapper知道如何將複雜的JavaScript物件寫入線性記憶體。然後,當WebAssembly函式返回一個值時,JavaScript wrapper將從線性記憶體中獲取資料並將其重新轉換為JavaScript物件。

【JSConf EU 2018】Rust + WebAssembly

要做到這一點,它會檢視Rust程式碼中的函式簽名,並計算出所需的JavaScript。這適用於像字串這樣的內建型別,也適用於你定義在程式碼裡的型別。wasm-bindgen將Rust結構體轉化JavaScript的類。該工具在當前版本只支援Rust,之後會不斷完善從而支援其他程式語言(例如:C/C++)。

這個專案是開始學習WebAssembly的很好的開始,因為我們可以通過自己編寫的WebAssembly模組來對真實世界產生影響。是不是很酷、很有趣。想體驗通過寫WebAssembly模組來控制燈光動畫的樂趣嗎?Mozilla已經幫我們準備了線上環境The Arch,現在就開始體驗吧!

相關文章