身為 WebGL 開發人員,您可能會對開始使用 WebGPU 感到既緊張又興奮,因為 WebGPU 是 WebGL 的後繼者,可將新式圖形 API 的進展帶入網頁。
您不必擔心,因為 WebGL 和 WebGPU 共用許多核心概念。這兩種 API 都能讓您在 GPU 上執行稱為著色器的小型程式。WebGL 支援頂點和片段著色器,而 WebGPU 也支援運算著色器。WebGL 使用 OpenGL 著色語言 (GLSL),而 WebGPU 則使用 WebGPU 著色語言 (WGSL)。雖然兩種語言不同,但基礎概念大致相同。
有鑑於此,本文將著重介紹 WebGL 和 WebGPU 之間的差異,協助您開始使用。
全域狀態
WebGL 有許多全域狀態。部分設定適用於所有轉譯作業,例如綁定哪些紋理和緩衝區。您可以透過呼叫各種 API 函式來設定這個全域狀態,且在您變更之前,該狀態會持續有效。WebGL 中的全域狀態是主要錯誤來源,因為很容易忘記變更全域設定。此外,全域狀態會導致程式碼共用作業變得困難,因為開發人員必須小心謹慎,避免不小心變更全域狀態,進而影響程式碼的其他部分。
WebGPU 是無狀態 API,不會維護全域狀態。而是使用管道的概念,封裝 WebGL 中全域的所有轉譯狀態。管道包含要使用的混合、拓樸和屬性等資訊。管道無法變更。如要變更部分設定,您必須建立另一個管道。WebGPU 也會使用指令編碼器將指令分批處理,並依照記錄的順序執行。這在陰影對應中很實用,例如在單一物件傳遞中,應用程式可以為每個光源陰影地圖記錄多個指令串流。
總而言之,由於 WebGL 的全球狀態模型會使建立可組合且穩定的程式庫和應用程式變得困難且不穩定,WebGPU 大幅減少了開發人員在向 GPU 傳送指令時需要追蹤的狀態數量。
不再同步
在 GPU 上,同步傳送指令並等待指令執行通常效率不彰,因為這可能會刷新管道並導致氣泡。這點在 WebGPU 和 WebGL 中尤其適用,因為這兩者採用多程序架構,且 GPU 驅動程式會在與 JavaScript 分開的程序中執行。
舉例來說,在 WebGL 中,呼叫 gl.getError()
需要從 JavaScript 程序到 GPU 程序,再從 GPU 程序返回的同步 IPC。這可能會導致兩個程序在通訊時,在 CPU 端產生一個氣泡。
為避免這些氣泡,WebGPU 的設計是完全非同步的。錯誤模型和所有其他作業都會非同步執行。舉例來說,建立紋理時,即使紋理實際上有誤,作業仍會立即成功。您只能以非同步方式發現錯誤。這項設計可避免跨程序通訊產生泡沫,並提供可靠的應用程式效能。
運算著色器
運算著色器是在 GPU 上執行的程式,用於執行一般用途的運算作業。但僅適用於 WebGPU,而非 WebGL。
與頂點和片段著色器不同,此類著色器不限於圖形處理,可用於各種工作,例如機器學習、物理模擬和科學運算。運算著色器會由數百或數千個執行緒平行執行,因此非常適合處理龐大的資料集。如要進一步瞭解 GPU 運算,請參閱 這篇詳細的 WebGPU 相關文章。
影片影格處理
使用 JavaScript 和 WebAssembly 處理影片影格有幾個缺點:從 GPU 記憶體複製資料到 CPU 記憶體的成本,以及工作站和 CPU 執行緒可達到的並行處理能力有限。WebGPU 沒有這些限制,因此與 WebCodecs API 緊密整合,非常適合處理影片影格。
以下程式碼片段說明如何在 WebGPU 中將 VideoFrame 匯入為外部紋理,並加以處理。您可以試用這個示範。
// Init WebGPU device and pipeline...
// Configure canvas context...
// Feed camera stream to video...
(function render() {
const videoFrame = new VideoFrame(video);
applyFilter(videoFrame);
requestAnimationFrame(render);
})();
function applyFilter(videoFrame) {
const texture = device.importExternalTexture({ source: videoFrame });
const bindgroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: texture }],
});
// Finally, submit commands to GPU
}
預設的應用程式可攜性
WebGPU 會強制要求 limits
。根據預設,requestDevice()
會傳回 GPUDevice,但該 GPUDevice 可能與實體裝置的硬體功能不符,而是所有 GPU 的合理且最低的共同分母。要求開發人員提出裝置限制,WebGPU 就能確保應用程式可在盡可能多的裝置上執行。
處理畫布
建立 WebGL 內容並提供內容屬性 (例如 alpha、antialias、 colorSpace、depth、preserveDrawingBuffer 或 stencil) 後,WebGL 會自動管理畫布。
另一方面,WebGPU 則需要您自行管理畫布。舉例來說,如要在 WebGPU 中實現反鋸齒效果,您可以建立多重取樣紋理並進行算繪。接著,您可以將多重取樣紋理解析為一般紋理,並將該紋理繪製至畫布。透過手動管理,您可以從單一 GPUDevice 物件輸出任意數量的畫布。相較之下,WebGL 只能為每個畫布建立一個內容。
請查看 WebGPU 多個畫布示範。
順帶一提,目前瀏覽器對每個網頁的 WebGL 畫布數量設有限制。在撰寫本文時,Chrome 和 Safari 最多只能同時使用 16 個 WebGL 畫布,Firefox 最多可建立 200 個。另一方面,每個網頁的 WebGPU 畫布數量則沒有限制。
實用的錯誤訊息
WebGPU 會為從 API 傳回的每則訊息提供呼叫堆疊。也就是說,您可以快速查看程式碼中發生錯誤的位置,這對於偵錯和修正錯誤來說非常實用。
除了提供呼叫堆疊,WebGPU 錯誤訊息也易於理解且可採取行動。錯誤訊息通常會說明錯誤內容,並提供修正建議。
WebGPU 也允許您為每個 WebGPU 物件提供自訂 label
。瀏覽器會在 GPUError 訊息、主控台警告和瀏覽器開發人員工具中使用這個標籤。
從名稱到索引
在 WebGL 中,許多項目都是透過名稱連結。舉例來說,您可以在 GLSL 中宣告名為 myUniform
的統一變數,並使用 gl.getUniformLocation(program, 'myUniform')
取得其位置。這很實用,因為如果您輸入的統一變數名稱有誤,系統會顯示錯誤訊息。
另一方面,在 WebGPU 中,所有內容都會透過位元組偏移或索引 (通常稱為「位置」) 完全連結。您有責任確保 WGSL 和 JavaScript 中的程式碼位置保持同步。
Mipmap 產生
在 WebGL 中,您可以建立紋理的第 0 級 MIP,然後呼叫 gl.generateMipmap()
。WebGL 會為您產生所有其他 MIP 層級。
在 WebGPU 中,您必須自行產生 mipmap。系統沒有內建函式可執行此操作。如要進一步瞭解這項決定,請參閱規格討論內容。您可以使用 webgpu-utils 等方便的程式庫產生 mipmap,也可以瞭解如何自行產生 mipmap。
儲存體緩衝區和儲存紋理
WebGL 和 WebGPU 都支援統一緩衝區,可讓您將大小受限的常數參數傳遞至著色器。儲存緩衝區與統一緩衝區非常相似,但只有 WebGPU 支援,且比統一緩衝區更強大、更具彈性。
傳遞至著色器的儲存緩衝區資料可能比統一緩衝區大得多。雖然規格說明指出,統一緩衝區繫結的大小上限為 64 KB (請參閱
maxUniformBufferBindingSize
),但 WebGPU 中的儲存緩衝區繫結大小上限至少為 128 MB (請參閱maxStorageBufferBindingSize
)。儲存空間緩衝區可寫入,且支援部分原子作業,而統一緩衝區則僅為唯讀。這可讓您實作新的演算法類別。
儲存體緩衝區繫結可支援執行階段大小陣列,以便提供更靈活的演算法,但必須在著色器中提供統一緩衝區陣列大小。
WebGPU 僅支援儲存紋理,而儲存紋理對紋理的作用,就如同儲存緩衝區對統一緩衝區的作用。這類材質比一般材質更具彈性,可支援隨機存取寫入 (日後也支援讀取)。
緩衝區和紋理變更
在 WebGL 中,您可以建立緩衝區或紋理,然後隨時使用 gl.bufferData()
和 gl.texImage2D()
分別變更其大小。
在 WebGPU 中,緩衝區和紋理皆為不可變動。也就是說,您無法在建立後變更其大小、用途或格式。您只能變更內容。
空格規則差異
在 WebGL 中,Z 剪輯空間範圍為 -1 到 1。在 WebGPU 中,Z 剪輯空間範圍為 0 到 1。也就是說,z 值為 0 的物體離相機最近,而 z 值為 1 的物體離相機最遠。
WebGL 採用 OpenGL 慣例,其中 Y 軸向上,Z 軸朝向觀眾。WebGPU 採用 Metal 慣例,其中 Y 軸向下,Z 軸則在螢幕外。請注意,在幀緩衝區座標、視窗座標和片段/像素座標中,Y 軸方向為向下。在剪輯空間中,Y 軸方向仍與 WebGL 相同。
特別銘謝
感謝 Corentin Wallez、Gregg Tavares、Stephen White、Ken Russell 和 Rachel Andrew 審查本文。
另外,建議您參閱 WebGPUFundamentals.org,深入瞭解 WebGPU 和 WebGL 之間的差異。