WebGPU の新機能(Chrome 121)

François Beaufort
François Beaufort

Android で WebGPU をサポート

Chrome チームは、Qualcomm と ARM の GPU を搭載し、Android 12 以降を搭載するデバイスで、Chrome 121 で WebGPU がデフォルトで有効になったことをお知らせいたします。

サポートは段階的に拡大され、まもなく Android 11 を搭載したデバイスなど、より幅広い Android デバイスが対象となります。今後、幅広いハードウェア構成でシームレスなエクスペリエンスを実現できるよう、さらなるテストと最適化を進めていく予定です。問題 chromium:1497815 をご覧ください。

Android 版 Chrome で実行されている WebGPU サンプルのスクリーンショット。
Android 版 Chrome で実行されている WebGPU サンプル。

Windows でシェーダーのコンパイルに FXC ではなく DXC を使用する

Chrome では、SM6+ グラフィック ハードウェアを搭載した Windows D3D12 マシンでシェーダーをコンパイルするために DXC(DirectX コンパイラ)の機能を使用できるようになりました。以前、WebGPU は Windows でのシェーダー コンパイルに FXC(FX コンパイラ)を使用していました。FXC は機能的には問題ありませんでしたが、DXC に存在する機能セットとパフォーマンスの最適化が欠落していました。

初期テストでは、DXC を使用すると FXC と比較してコンピューティング シェーダーのコンパイル速度が平均 20% 向上することが示されています。

コンピューティング パスとレンダリング パスでのタイムスタンプ クエリ

タイムスタンプ クエリを使用すると、WebGPU アプリケーションは、GPU コマンドがコンピューティング パスとレンダリング パスの実行に要する時間を(ナノ秒単位で)正確に測定できます。GPU ワークロードのパフォーマンスと動作に関する分析情報を取得するために広く使用されています。

"timestamp-query" 機能が GPUAdapter で利用可能になると、次のことができるようになります。

  • "timestamp-query" 機能を使用して GPUDevice をリクエストします。
  • "timestamp" タイプの GPUQuerySet を作成します。
  • GPUComputePassDescriptor.timestampWritesGPURenderPassDescriptor.timestampWrites を使用して、GPUQuerySet にタイムスタンプ値を書き込む場所を定義します。
  • タイムスタンプ値を resolveQuerySet()GPUBuffer に解決します。
  • 結果を GPUBuffer から CPU にコピーして、タイムスタンプ値を読み取ります。
  • タイムスタンプ値を BigInt64Array としてデコードします。

次の例と問題 dawn:1800 をご覧ください。

const adapter = await navigator.gpu.requestAdapter();
if (!adapter.features.has("timestamp-query")) {
  throw new Error("Timestamp query feature is not available");
}
// Explicitly request timestamp query feature.
const device = await adapter.requestDevice({
  requiredFeatures: ["timestamp-query"],
});
const commandEncoder = device.createCommandEncoder();

// Create a GPUQuerySet which holds 2 timestamp query results: one for the
// beginning and one for the end of compute pass execution.
const querySet = device.createQuerySet({ type: "timestamp", count: 2 });
const timestampWrites = {
  querySet,
  beginningOfPassWriteIndex: 0, // Write timestamp in index 0 when pass begins.
  endOfPassWriteIndex: 1, // Write timestamp in index 1 when pass ends.
};
const passEncoder = commandEncoder.beginComputePass({ timestampWrites });
// TODO: Set pipeline, bind group, and dispatch work to be performed.
passEncoder.end();

// Resolve timestamps in nanoseconds as a 64-bit unsigned integer into a GPUBuffer.
const size = 2 * BigInt64Array.BYTES_PER_ELEMENT;
const resolveBuffer = device.createBuffer({
  size,
  usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,
});
commandEncoder.resolveQuerySet(querySet, 0, 2, resolveBuffer, 0);

// Read GPUBuffer memory.
const resultBuffer = device.createBuffer({
  size,
  usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
});
commandEncoder.copyBufferToBuffer(resolveBuffer, 0, resultBuffer, 0, size);

// Submit commands to the GPU.
device.queue.submit([commandEncoder.finish()]);

// Log compute pass duration in nanoseconds.
await resultBuffer.mapAsync(GPUMapMode.READ);
const times = new BigInt64Array(resultBuffer.getMappedRange());
console.log(`Compute pass duration: ${Number(times[1] - times[0])}ns`);
resultBuffer.unmap();

タイミング攻撃の懸念があるため、タイムスタンプ クエリは 100 マイクロ秒の解像度で量子化されます。これにより、精度とセキュリティのバランスが取れています。Chrome ブラウザでは、アプリの開発中に chrome://flags/#enable-webgpu-developer-features で「WebGPU デベロッパー向け機能」フラグを有効にすることで、タイムスタンプの量子化を無効にできます。詳しくは、タイムスタンプ クエリの量子化をご覧ください。

GPU がタイムスタンプ カウンタをリセットすることがあるため、タイムスタンプ間の負の差分などの予期しない値が返される可能性があります。次の Compute Boids サンプルにタイムスタンプ クエリのサポートを追加する git diff の変更を確認することをおすすめします。

タイムスタンプ クエリを備えた Compute Boids サンプルのスクリーンショット。 タイムスタンプ クエリを備えた Compute Boids のサンプル。

シェーダー モジュールのデフォルトのエントリ ポイント

デベロッパー エクスペリエンスを改善するため、コンピューティング パイプラインまたはレンダリング パイプラインを作成するときに、シェーダー モジュールの entryPoint を省略できるようになりました。シェーダー コードでシェーダー ステージの一意のエントリ ポイントが見つからない場合、GPUValidationError がトリガーされます。次の例と 問題 dawn:2254 をご覧ください。

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 module = myDevice.createShaderModule({ code });
const format = navigator.gpu.getPreferredCanvasFormat();
const pipeline = await myDevice.createRenderPipelineAsync({
  layout: "auto",
  vertex: { module, entryPoint: "vertexMain" },
  fragment: { module, entryPoint: "fragmentMain", targets: [{ format }] },
  vertex: { module },
  fragment: { module, targets: [{ format }] },
});

GPUExternalTexture 色空間として display-p3 をサポート

importExternalTexture() を使用して HDR 動画から GPUExternalTexture をインポートするときに、"display-p3" の宛先色空間を設定できるようになりました。WebGPU がカラースペースを処理する方法を確認する。次の例と chromium:1330250 の問題をご覧ください。

// Create texture from HDR video.
const video = document.querySelector("video");
const texture = myDevice.importExternalTexture({
  source: video,
  colorSpace: "display-p3",
});

メモリヒープの情報

アプリの開発中に大量のメモリを割り当てる際のメモリ制限を予測できるように、requestAdapterInfo() で、アダプタで使用可能なメモリヒープのサイズやタイプなどの memoryHeaps 情報を公開できるようになりました。この試験運用版機能は、chrome://flags/#enable-webgpu-developer-features の [WebGPU デベロッパー機能] フラグが有効になっている場合にのみアクセスできます。次の例と 問題 dawn:2249 をご覧ください。

const adapter = await navigator.gpu.requestAdapter();
const adapterInfo = await adapter.requestAdapterInfo();

for (const { size, properties } of adapterInfo.memoryHeaps) {
  console.log(size); // memory heap size in bytes
  if (properties & GPUHeapProperty.DEVICE_LOCAL)  { /* ... */ }
  if (properties & GPUHeapProperty.HOST_VISIBLE)  { /* ... */ }
  if (properties & GPUHeapProperty.HOST_COHERENT) { /* ... */ }
  if (properties & GPUHeapProperty.HOST_UNCACHED) { /* ... */ }
  if (properties & GPUHeapProperty.HOST_CACHED)   { /* ... */ }
}
アダプター情報にメモリヒープが表示されている https://webgpureport.org のスクリーンショット。
https://webgpureport.org に表示されるアダプター情報メモリヒープ。

Dawn の更新

WGSL 言語機能を処理するために、wgpu::InstanceHasWGSLLanguageFeature メソッドと EnumerateWGSLLanguageFeatures メソッドが追加されました。問題 dawn:2260 を参照。

標準以外の wgpu::Feature::BufferMapExtendedUsages 機能を使用すると、wgpu::BufferUsage::MapRead または wgpu::BufferUsage::MapWrite と他の wgpu::BufferUsage を使用して GPU バッファを作成できます。次の例と問題 dawn:2204 をご覧ください。

wgpu::BufferDescriptor descriptor = {
  .size = 128,
  .usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::Uniform
};
wgpu::Buffer uniformBuffer = device.CreateBuffer(&descriptor);

uniformBuffer.MapAsync(wgpu::MapMode::Write, 0, 128,
   [](WGPUBufferMapAsyncStatus status, void* userdata)
   {
      wgpu::Buffer* buffer = static_cast<wgpu::Buffer*>(userdata);
      memcpy(buffer->GetMappedRange(), data, sizeof(data));
   },
   &uniformBuffer);

次の機能がドキュメント化されています。ANGLE テクスチャ共有D3D11 マルチスレッド保護暗黙のデバイス同期Norm16 テクスチャ形式パス内のタイムスタンプ クエリピクセル ローカル ストレージシェーダー機能マルチ プレーン形式

Chrome チームは、Dawn の公式 GitHub リポジトリを作成しました。

以下に、主なハイライトをいくつかご紹介します。コミットの一覧(すべて網羅)をご覧ください。

WebGPU の新機能

WebGPU の新機能シリーズで取り上げられたすべての内容のリスト。

Chrome 131

Chrome 130

Chrome 129

Chrome 128

Chrome 127

Chrome 126

Chrome 125

Chrome 124

Chrome 123

Chrome 122

Chrome 121

Chrome 120

Chrome 119

Chrome 118

Chrome 117

Chrome 116

Chrome 115

Chrome 114

Chrome 113