WebGPU:在瀏覽器中解鎖新型 GPU 存取權

瞭解 WebGPU 如何發揮 GPU 的強大力量,提升機器學習效能和圖形算繪品質。

Corentin Wallez
Corentin Wallez
François Beaufort
François Beaufort

新的 WebGPU API 可大幅提升圖像和機器學習工作負載的效能。本文將探討 WebGPU 如何改善目前的 WebGL 解決方案,並搶先一窺未來的發展。不過,我們先來談談 WebGPU 的開發背景。

WebGPU 上的背景

WebGL 於 2011 年在 Chrome 推出。透過讓網頁應用程式充分利用 GPU,WebGL 可在網路上提供絕佳體驗,包括 Google 地球、互動式音樂影片和 3D 房地產導覽等。WebGL 是以 OpenGL 系列 API 為基礎,該系列 API 最早在 1992 年開發。那是好久以前的事了!您可以想像,GPU 硬體自那時以來已大幅進化。

為了跟上這項演進,我們開發了新一代的 API,以更有效率的方式與現代 GPU 硬體互動。例如 Direct3D 12MetalVulkan 等 API。這些新的 API 支援 GPU 程式設計的新用途,例如機器學習的爆炸式成長和算繪演算法的進步。WebGPU 是 WebGL 的後繼者,可將這類新型 API 的進階功能帶入網際網路。

WebGPU 可在瀏覽器中開啟許多新的 GPU 程式設計可能性。這項更新更能反映現代 GPU 硬體的運作方式,同時也為日後更進階的 GPU 功能奠定基礎。自 2017 年起,這個 API 就已在 W3C 的「GPU for the Web」 群組中開發,並與 Apple、Google、Mozilla、Microsoft 和 Intel 等多家公司合作。經過 6 年的努力,我們很高興終於推出網頁平台的重大更新!

目前,ChromeOS、macOS 和 Windows 版 Chrome 113 已支援 WebGPU,其他平台也即將推出支援。在此特別感謝其他 Chromium 貢獻者,以及協助我們實現這項功能的 Intel。

接下來,我們來看看 WebGPU 支援的幾種令人興奮的用途。

發掘新的 GPU 工作負載,用於算繪

WebGPU 功能 (例如運算著色器) 可讓新類型的演算法移植至 GPU。舉例來說,演算法可為場景加入更多動態細節、模擬物理現象等等。甚至還有先前只能使用 JavaScript 執行的工作負載,現在也能移至 GPU。

以下影片顯示如何使用等邊立方體演算法,將這些元球的表面分割成三角形。在影片的前 20 秒,當演算法在 JavaScript 中執行時,由於網頁僅以 8 FPS 的速度運作,因此無法順利執行動畫,導致動畫出現卡頓現象。為了在 JavaScript 中維持效能,我們需要大幅降低詳細程度。

將相同演算法移至運算著色器後,兩者的速度天差地遠,這點可在影片的 20 秒處看到。效能大幅提升,網頁現在以流暢的 60 FPS 運作,且仍有大量效能空間可用於其他效果。此外,頁面的主要 JavaScript 迴圈會完全釋出,以便執行其他工作,確保與頁面的互動反應速度維持一致。

Metaballs 示範

WebGPU 也能實現先前無法實現的複雜視覺效果。在以下範例中,我們使用熱門的 Babylon.js 程式庫,完全在 GPU 上模擬海洋表面。透過彼此疊加許多獨立的波浪,就能產生逼真的動態效果。但直接模擬每個波形的成本太高。

海洋示範

因此,這個示範使用了稱為「快速傅立葉轉換」的進階演算法。這項功能不會將所有波浪都表示為複雜的位置資料,而是使用光譜資料,這類資料可更有效率地執行運算。接著,每個影格都會使用傅立葉變換,將光譜資料轉換為代表波浪高度的位置資料。

加快機器學習推論速度

WebGPU 也能用於加快機器學習的速度,這也是近年來 GPU 的主要用途。

長久以來,創意開發人員一直將 WebGL 的算繪 API 重新用於執行非算繪作業,例如機器學習運算。不過,這需要繪製三角形的像素,以便啟動運算,並在紋理中小心地打包和解包張量資料,而不是使用更通用的記憶體存取方式。

這張圖片說明使用 WebGL 執行單一 ML 運算子的效率不佳情形,包括多餘的記憶體負載、多餘的運算,以及每個執行緒寫入的值很少。
使用 WebGL 執行單一機器學習運算子。

以這種方式使用 WebGL 會讓開發人員必須尷尬地讓程式碼符合 API 的預期,而該 API 只設計用於繪圖。加上缺乏基本功能 (例如在運算之間共用記憶體存取),因此會導致重複作業和效能不佳。

運算著色器是 WebGPU 的主要新功能,可解決這些問題。運算著色器提供更彈性的程式設計模式,可充分利用 GPU 的大量平行特性,同時不受算繪作業嚴格結構的限制。

WebGPU 運算著色器的各種效率提升,包括共用記憶體載入、共用運算,以及靈活的記憶體寫入。
WebGPU 運算著色器效率。

運算著色器可讓您在不同著色器工作群組之間共用資料和運算結果,提高效率。這比先前嘗試使用 WebGL 達到相同目的時,可獲得更顯著的效益。

舉例來說,在 TensorFlow.js 中,圖片擴散模型的初始移植作業從 WebGL 移至 WebGPU 後,在各種硬體上顯示的效能提升了 3 倍。在經過測試的部分硬體上,圖片的算繪時間不到 10 秒。由於這是早期移植版本,我們相信 WebGPU 和 TensorFlow.js 還有更多改善空間!請參閱「2023 年 Web ML 的最新功能」一文。Google I/O 工作階段。

不過,WebGPU 不僅是將 GPU 功能帶入網際網路。

以 JavaScript 為優先設計

這些功能可讓特定平台的桌面和行動開發人員支援這些用途,我們也一直在努力讓這些功能在網頁平台上自然呈現。

WebGPU 的開發過程,是開發人員在過去十年內使用 WebGL 完成精彩作品後,回顧當時的經驗所得出的成果。我們將他們遇到的問題、遇到的瓶頸和提出的問題,全都納入這項新 API 的意見回饋中。

我們發現,WebGL 的全球狀態模型會導致建立健全的可組合函式庫和應用程式變得困難且不穩定。因此,WebGPU 大幅減少了開發人員在傳送 GPU 指令時需要追蹤的狀態數量。

我們瞭解到偵錯 WebGL 應用程式相當麻煩,因此 WebGPU 包含更靈活的錯誤處理機制,不會影響效能。我們會盡力確保您從 API 收到的每則訊息都簡單易懂且可採取行動

我們也發現,經常發生太多 JavaScript 呼叫的額外負擔,是複雜 WebGL 應用程式的瓶頸。因此,WebGPU API 的閒聊程度較低,您可以透過較少的函式呼叫完成更多工作。我們會著重於事先執行重量驗證,盡可能讓關鍵繪製迴圈保持精簡。此外,我們也提供 算繪套件等新的 API,可讓您預先記錄大量繪圖指令,並透過單一呼叫重播這些指令。

為了展示轉譯組件等功能所帶來的巨大差異,這裡提供另一個 Babylon.js 的示範。他們的 WebGL 2 轉譯器可以執行所有 JavaScript 呼叫,每秒轉譯此藝廊場景約 500 次。這很不錯!

藝廊

不過,他們的 WebGPU 轉譯器啟用了他們稱為「快照轉譯」的功能。這項功能以 WebGPU 算繪套件為基礎,可讓相同場景的提交速度提高 10 倍以上。這項功能大幅減少額外負擔,讓 WebGPU 能夠算繪更複雜的場景,同時讓應用程式可並行執行更多 JavaScript 作業。

現代圖形 API 以複雜度聞名,為了極致最佳化,犧牲了簡易性。另一方面,WebGPU 則著重於跨平台相容性,在大多數情況下自動處理資源同步等傳統上較難處理的議題。

這也帶來了一個好處,就是 WebGPU 很容易學習和使用。它會依賴網路平台的現有功能,處理圖片和影片載入等作業,並採用知名的 JavaScript 模式,例如用於非同步作業的 Promise。這有助於盡量減少所需的樣板程式碼數量。您可以透過不到 50 行程式碼,在畫面上顯示第一個三角形。

<canvas id="canvas" width="512" height="512"></canvas>
<script type="module">
  const adapter = await navigator.gpu.requestAdapter();
  const device = await adapter.requestDevice();

  const context = canvas.getContext("webgpu");
  const format = navigator.gpu.getPreferredCanvasFormat();
  context.configure({ device, format });

  const code = `
    @vertex fn vertexMain(@builtin(vertex_index) i : u32) ->
      @builtin(position) vec4f {
       const pos = array(vec2f(0, 1), vec2f(-1, -1), vec2f(1, -1));
       return vec4f(pos[i], 0, 1);
    }
    @fragment fn fragmentMain() -> @location(0) vec4f {
      return vec4f(1, 0, 0, 1);
    }`;
  const shaderModule = device.createShaderModule({ code });
  const pipeline = device.createRenderPipeline({
    layout: "auto",
    vertex: {
      module: shaderModule,
      entryPoint: "vertexMain",
    },
    fragment: {
      module: shaderModule,
      entryPoint: "fragmentMain",
      targets: [{ format }],
    },
  });
  const commandEncoder = device.createCommandEncoder();
  const colorAttachments = [
    {
      view: context.getCurrentTexture().createView(),
      loadOp: "clear",
      storeOp: "store",
    },
  ];
  const passEncoder = commandEncoder.beginRenderPass({ colorAttachments });
  passEncoder.setPipeline(pipeline);
  passEncoder.draw(3);
  passEncoder.end();
  device.queue.submit([commandEncoder.finish()]);
</script>

結論

我們很高興看到 WebGPU 為網路平台帶來各種新可能,也期待看到各位開發人員為 WebGPU 找到各種新奇的用途!

以 WebGL 為基礎,我們已建立了充滿活力的程式庫和架構生態系統,而這個生態系統也非常樂意採用 WebGPU。許多熱門的 JavaScript WebGL 程式庫目前正在進行或已完成 WebGPU 支援,在某些情況下,只要變更單一標記,就能享有 WebGPU 的優點!

Babylon.js、Construct 3、Google 地球、Google Meet、PlayCanvas、Sketchfab、Three.JS、TensorFlow.js 和 Unity。
已完成或正在進行 WebGPU 移植的架構、應用程式和程式庫。

Chrome 113 的首個版本只是起步,雖然我們最初的版本適用於 Windows、ChromeOS 和 macOS,但我們預計在近期將 WebGPU 引進其他平台,例如 Android 和 Linux。

而且,不只是 Chrome 團隊一直致力於推出 WebGPU。我們也正在 Firefox 和 WebKit 中實作這項功能。

此外,W3C 也正在設計新功能,可在硬體推出時公開。舉例來說,我們預計很快會在 Chrome 中啟用著色器中的 16 位元浮點數支援DP4a 類別指令,進一步改善機器學習效能。

WebGPU 是廣泛的 API,只要投入心力,就能發揮驚人的效能。今天我們只能概略介紹 WebGPU 的優點,但如果您想開始使用 WebGPU,請參閱我們的入門程式碼研究室 第一個 WebGPU 應用程式。在這個程式碼研究室中,您將建構經典 Conway's Game of Life 的 GPU 版本。這個程式碼研究室會逐步引導您完成這個程序,因此即使您是第一次進行 GPU 開發,也能嘗試操作。

WebGPU 範例也是體驗 API 的好地方。從傳統的「hello triangle」到更完整的轉譯和運算管道,展示各種技術。最後,請參閱其他資源