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 をご覧ください。

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

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

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

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

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

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

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

  • "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 Developer Features」フラグを有効にすると、タイムスタンプ量子化を無効にできます。詳しくは、タイムスタンプ クエリの量子化をご覧ください。

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

タイムスタンプ クエリを含む Compute Boids サンプルのスクリーンショット。
タイムスタンプ クエリを使用した Compute Boids のサンプル。

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

デベロッパー エクスペリエンスを向上させるため、コンピューティング パイプラインまたはレンダリング パイプラインを作成するときに、シェーダー モジュールの entryPoint を省略できるようになりました。シェーダーのコード内にシェーダー ステージへの一意のエントリ ポイントがない場合、GPUValidationError がトリガーされます。次の例と issue 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",
});

メモリヒープ情報

アプリ開発中に大量のメモリを割り当てる際のメモリ制限を予測できるように、アダプターで使用可能なメモリヒープのサイズやタイプなどの memoryHeaps 情報が requestAdapterInfo() で公開されるようになりました。この試験運用版の機能は、chrome://flags/#enable-webgpu-developer-features で「WebGPU Developer Features」フラグが有効になっている場合のみ使用できます。次の例と issue 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 に表示されるアダプタ情報のメモリヒープ。

夜明けの更新情報

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 テクスチャ形式パス内のタイムスタンプ クエリGoogle Pixel ローカル ストレージシェーダー機能マルチプレーン形式が文書化されています。

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

ここでは、重要なハイライトの一部についてのみ説明します。commit の完全なリストを確認する。

WebGPU の新機能

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

Chrome 124

Chrome 123

Chrome 122

Chrome 121

Chrome 120

Chrome 119

Chrome 118

Chrome 117

Chrome 116

Chrome 115

Chrome 114

Chrome 113