1. 獲取高頻操作對象
1.1 WebGL 獲取上下文對象
WebGL 獲取的是 WebGLRenderingContext/WebGLRenderingContext2
對象,必須依賴於有合適寬度和高度的 HTMLCanvasElement
,通常命名爲 gl
,gl 變量有非常多方法,允許修改 WebGL 的全局狀態
const gl = document.getElementById("id")?.getContext("webgl")
// ...
1.2 WebGPU 獲取設備對象
而 WebGPU 則不依賴具體的 Canvas,它操作的是物理圖形卡設備,並使用 ES6/7
的異步語法獲取,獲取的是 GPUAdapter
和 GPUDevice
,但是與 WebGLRenderingContext
起着類似“發出大多數命令”的大管家式角色的,更多是 GPUDevice
對象
const entryFn = async () => {
if (!navigator.gpu) {
return
}
// 測試版 Chrome 有可能返回 null
const adapter = await navigator.gpu.requestAdapter()
if (!adapter) {
return
}
const device = await adapter.requestDevice()
// ...
}
entryFn()
WebGPU 的入口是 navigator.gpu
對象,這個對象在 WebWorker 中也有,所以對 CPU 端的多線程有良好的支持。使用此對象異步請求適配器後,再使用適配器請求具象化的設備對象即可。
至於“適配器”和“設備”的概念界定,需要讀者自行閱讀 WebGPU Explainer、WebGPU Specification Core Object 等資料,前者大概是物理設備的一個變量符號,而根據不同的場景、線程需求再次請求“設備”,此設備並非物理設備,只是一個滿足代碼上下文所需要條件的、更實際的“對象”。
每次請求的適配器對象是不同的,不具備單例特徵。
設備對象用於創建 WebGPU 中幾乎所有的子類型,包括 GPUBuffer
、GPUTexture
等,以及訪問一些自有屬性,例如隊列屬性 device.queue
.
2. 初始化參數的異同
2.1 WebGL
在 WebGLRenderingContext 時,允許傳遞一些參數:
const gl = canvasEle.getContext("webgl", {
alpha: false, // 是否包含透明度緩存區
antialias: false, // 是否開抗鋸齒
depth: false, // 是否包含一個16位的深度緩衝區
stencil: false, // 是否包含一個8位的模板緩衝區
failIfMajorPerformanceCaveat: false, // 在系統性能低的環境中是否創建上下文
powerPreference: "high-performance", // GPU電源配置,"high-performance" 是高性能
preserveDrawingBuffer: false, // 是否保留緩衝區
premultipliedAlpha: false, // 是否預乘透明度通道
})
2.2 WebGPU 分兩步
2.2.1 GPUAdapter
在請求 WebGPU 的適配器時,保留了性能選項(當前規範)powerPreference:
// in async function
const adapter = await navigator.gpu.requestAdapter({
powerPreference: "high-performance",
})
關於 requestAdapter 方法的參數,其類型 GPURequestAdapterOptions
定義,見下:
dictionary GPURequestAdapterOptions {
GPUPowerPreference powerPreference;
boolean forceFallbackAdapter = false;
};
enum GPUPowerPreference {
"low-power",
"high-performance",
};
forceFallbackAdapter
參數用得不多,有需要的讀者可自行查詢官方文檔。
2.2.2 GPUDevice
請求設備對象時,則允許傳入 GPUDeviceDescriptor
參數對象,該對象允許有兩個可選參數,一個是 requiredFeatures
,類型爲 string[]
,另一個是 requiredLimits
,類型是鍵爲 string 值爲 number 的對象:
dictionary GPUDeviceDescriptor : GPUObjectDescriptorBase {
sequence<GPUFeatureName> requiredFeatures = [];
record<DOMString, GPUSize64> requiredLimits = {};
};
requireFeatures 數組的元素是字符串,不是隨便填的,要參考 WebGPU Spec 24 功能索引表 中的功能。傳遞這個功能數組,就意味着要向適配器請求有這麼多功能的設備對象;
requireLimits 則向圖形處理器請求判斷,我傳遞進來的這個要求,你能不能滿足。
如果超過了適配器的 limits,那麼請求將失敗,適配器的 requestDevice 方法將返回一個 reject 的 Promise;
如果傳入的限制條目的要求沒有比全局默認值更好(有“更大更好”和“更小更好”,參考 WebGPU 第3章 中有關 limits 的表述),那就返回帶默認值的設備對象,並 resolve Promise;
其中,限制條目有哪些,默認值是多少,對某個限制條目“更大值更好”還是“更小值更好”,要參考 WebGPU Spec 3.6 限制 中的表格。
上面這麼說會比較抽象,下面舉例說明。
例如下面這個例子,請求設備對象時,會問適配器能不能滿足我要求的條件:
- 最多要有 2 個綁定組(默認是4個,越大越好,顯然 2 < 4)
- 最多隻能有 4 個 UBO(默認12個,越大越好,顯然 4 < 12)
- 能不能滿足 2048 像素尺寸的 2D 紋理(默認 8192 像素,越大越好,顯然 2048 < 8192)
const device = await adapter.requestDevice({
maxBindGroups: 2,
maxUniformBuffersPerShaderStage: 4,
maxTextureDimension2D: 2048,
})
顯然,請求的這三個條件都滿足要求,返回的設備對象的限制列表都按所有限制條目的默認值來。
console.log(device.limits)
{
maxBindGroup: 4,
maxUniformBuffersPerShaderStage: 12,
maxTextureDimension2D: 8192,
// ...
}
關於這段,requiredLimits 的含義是“我的程序可能要這樣的要求,你這個適配器能不能滿足”,而不是“我要這麼多要求,你給我返回一個這些參數的設備對象”。設備的創建過程,在 WebGPU Specification 的第 3 章,核心對象 - 設備中有詳細描述。
3. 總結
WebGL 的請求參數包括了性能參數和功能參數,較爲簡單。
WebGPU 分成了兩個階段,請求適配器時可以對性能作要求,請求設備對象時可以對使用 GPU 時各個方面的參數作校驗能不能滿足程序要求。