WebGL 與 WebGPU 比對[2] - 初始化篇

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 的異步語法獲取,獲取的是 GPUAdapterGPUDevice,但是與 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 中幾乎所有的子類型,包括 GPUBufferGPUTexture 等,以及訪問一些自有屬性,例如隊列屬性 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 時各個方面的參數作校驗能不能滿足程序要求。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章