WebGPU API

Limited availability

This feature is not Baseline because it does not work in some of the most widely-used browsers.

Experimental: これは実験的な機能です。
本番で使用する前にブラウザー互換性一覧表をチェックしてください。

安全なコンテキスト用: この機能は一部またはすべての対応しているブラウザーにおいて、安全なコンテキスト (HTTPS) でのみ利用できます。

WebGPU API は、ウェブ開発者が下層のシステムの GPU (Graphics Processing Unit) を使用し、高効率の計算をしたり、ブラウザーでレンダーできる複雑な画像を描画したりすることを可能にします。

WebGPU は WebGL の後継で、最近の GPU とのより良い互換性を提供し、汎用 GPU 計算に対応し、操作を速くし、さらに高度な GPU の機能へのアクセスを可能にします。

概念と使用法

2011 年頃に最初に登場した後、WebGL がグラフィックの能力の面でウェブに革命を起こしたといえます。WebGL は OpenGL ES 2.0 グラフィックライブラリーの JavaScript への移植であり、ウェブページがレンダリング計算をデバイスの GPU に直接渡し、超高速で処理させ、結果を 要素内に描画することを可能にします。

WebGL と WebGL シェーダーのコードを書くのに用いられる GLSL 言語は複雑なので、WebGL アプリケーションをより簡単に書けるようにするためにいくつかの WebGL ライブラリーが作られました。有名な例としては Three.jsBabylon.jsPlayCanvas などがあります。開発者はこれらのツールを用い、没入感のあるウェブベースの 3D ゲーム、ミュージックビデオ、訓練やモデリングのツール、VR や AR の体験、などを作ってきました。

しかし、WebGL には修正が必要な根本的な問題点がいくつかあります。

  • WebGL がリリースされて以降、新世代のネイティブ GPU API が登場しました。最も人気があるのは Microsoft の Direct3D 12Apple の MetalThe Khronos Group の Vulkan です。これらは多くの新機能を提供します。OpenGL のアップデートはもう計画されておらず、WebGL も同様なので、これらの新機能は何も導入されません。一方、WebGPU は進歩し、新機能が追加されるでしょう。
  • WebGL は完全にグラフィックを描画し、それをキャンバスに描画するというユースケースに基づいており、汎用 GPPU (GPGPU) 計算をあまり上手く扱うことができません。GPGPU 計算は機械学習モデルをベースにするものなど、多くの異なるユースケースでどんどん重要になってきています。
  • 3D グラフィックアプリケーションは、同時にレンダリングするオブジェクトの数と新しいレンダリング機能の活用の両面で、負荷が高くなってきています。

WebGPU は、最近の GPU API と互換性があり、より「webby」な感じがする新しい汎用アーキテクチャを提供し、これらの問題点を解決します。グラフィックのレンダリングに対応しているとともに、GPGPU 計算にもよく対応しています。CPU 側での個別のオブジェクトの描画は劇的に軽くなり、計算ベースのパーティクルや、色効果、鮮明化、被写界深度シミュレーションなどの後処理フィルターなどの最近の GPU のレンダリング機能にも対応します。さらに、カリングやスキン付きモデルの変換などの重い計算を直接 GPU で扱うことができます。

一般モデル

デバイスの GPU と WebGPU API を実行しているウェブブラウザーの間には、いくつかの抽象化レイヤーがあります。WebGPU の学習を開始する際、これらを理解することは有用です。

デバイス上の WebGPU アーキテクチャの異なる要素の位置を示す基本の積層図

  • GPU がある物理デバイス。ほとんどのデバイスには GPU が 1 個だけありますが、複数あるデバイスもあります。以下の異なる GPU の種類が利用可能です。

    • 統合 GPU: CPU と同じ基板にあり、メモリーを共有します。
    • 個別 GPU: 独自の基板にあり、CPU からは分離されています。
    • ソフトウェア「GPU」: CPU 上で実装されています。

    メモ: 上記の図では、GPU が 1 個だけあるデバイスを仮定しています。

  • OS の一部であるネイティブ GPU API (たとえば macOS 上の Metal) は、ネイティブアプリケーションが GPU の機能を用いることができるプログラミングインターフェイスです。API 命令がドライバーを通じて GPU に送られ、結果を受け取ります。上記の図ではネイティブ API およびドライバーが 1 個だけあるデバイスを仮定していますが、システムが GPU とやり取りするための複数のネイティブ OS API やドライバーを持つことも可能です。

  • ブラウザーの WebGPU 実装は、ネイティブ GPU API ドライバーを通じた GPU とのやり取りを扱います。WebGPU のアダプターが、あなたのコード上で下層のシステムで利用可能な物理 GPU とドライバーを効率よく表します。

  • 論理デバイスは、単一のウェブアプリケーションが分離された方法で GPU の機能にアクセスできるようにする抽象化です。論理デバイスは、多重化の機能を提供する必要があります。物理デバイスの GPU は多くのアプリケーションで用いられ、並行で処理を行います。この中には多くのウェブアプリケーションが含まれる可能性があります。それぞれのウェブアプリケーションは、セキュリティおよびロジック上の理由で、隔離された状態で WebGPU にアクセスできる必要があります。

デバイスへのアクセス

論理デバイスは、GPUDevice オブジェクトインスタンスで表され、ウェブアプリケーションが WebGPU のすべての機能にアクセスする基礎となります。デバイスへのアクセスは、以下の手順で行われます。

  1. Navigator.gpu プロパティ (もしくは、ワーカーから WebGPU の機能を用いる場合は WorkerNavigator.gpu) が現在のコンテキスト用の GPU オブジェクトを返します。
  2. GPU.requestAdapter() メソッドを通じてアダプターにアクセスします。このメソッドは省略可能な設定オブジェクトを受け取り、たとえば高パフォーマンスのアダプターや低消費電力のアダプターを要求することができます。これが無い場合は、デバイスはデフォルトのアダプターへのアクセスを提供し、これはほとんどの目的に十分適するでしょう。
  3. GPUAdapter.requestDevice() によりデバイスを要求できます。このメソッドは、(ディスクリプターと呼ばれる) オプションオブジェクトも受け取り、これにより論理デバイスに期待する詳細な機能や制限を指定できます。これが無い場合は、返されるデバイスは合理的な汎用のスペックを持ち、これはほとんどの用途に適します。

これらにいくつかの機能検出チェックを加えると、上記の手順は以下のようにして実現できます。

js
async function init() {
  if (!navigator.gpu) {
    throw Error("WebGPU に対応していません。");
  }

  const adapter = await navigator.gpu.requestAdapter();
  if (!adapter) {
    throw Error("WebGPU アダプターの要求に失敗しました。");
  }

  const device = await adapter.requestDevice();

  //...
}

パイプラインとシェーダー: WebGPU アプリケーションの構造

パイプラインは、プログラムの処理を実現するために実行するプログラマブルなステージが入る論理的な構造です。現在、WebGPU では以下の 2 種類のパイプラインを扱うことができます。

  • レンダーパイプラインはグラフィックをレンダリングします。 要素に描画することが多いですが、オフスクリーンでグラフィックをレンダリングすることもできます。これには以下の 2 個のメインステージがあります。

    • バーテックスステージ: バーテックスシェーダーが GPU に渡された位置データを受け取り、回転、変換、射影などの指定の効果を適用することで 3D 空間内の頂点群の位置を決定します。そして、頂点は三角形 (レンダリングされるグラフィックの基礎となる部品) などのプリミティブに組み立てられ、GPU によって描画を行うキャンバスのどのピクセルをカバーするかを特定するためにラスタライズされます。

    • : フラグメントステージ: バーテックスシェーダーによって生成されたプリミティブでカバーされた各ピクセルの色をフラグメントシェーダーが計算します。これらの計算には、表面の詳細を提供する (テクスチャ形式の) 画像や、仮想光源の位置や色などの入力がよく用いられます。

  • コンピュートパイプラインは一般の計算用です。コンピュートパイプラインは 1 個の計算ステージからなります。このステージでは、コンピュートシェーダーが一般のデータを受け取り、指定の数のワークグループで並列計算を行い、結果を 1 個以上のバッファーで返します。バッファーには任意の種類のデータを置けます。

上記で言及されたシェーダーは、GPU で処理される命令の集合です。WebGPU のシェーダーは WebGPU Shader Language (WGSL) と呼ばれる Rust 風の低レベルの言語で書かれます。

WebGPU アプリケーションを構築するにはいくつかの異なる方法がありますが、このプロセスはおそらく以下の手順を含むでしょう。

  1. シェーダーモジュールの生成: WGSL でシェーダーコードを書き、1 個以上のシェーダーモジュールにパッケージ化します。
  2. キャンバスコンテキストの取得と設定: 要素の webgpu コンテキストを取得し、GPU 論理デバイスでどのような画像をレンダリングするかの情報を設定します。この手順は、コンピュートパイプラインのみを用いる場合など、アプリケーションが画像を出力しない場合は不要です。
  3. データを格納したリソースの生成: パイプラインで処理するデータは、アプリケーションからアクセスするため、GPU バッファーまたはテクスチャーに格納される必要があります。
  4. パイプラインの生成: 必要なデータ構造、バインディング、シェーダー、リソースの配置を含めて要求するパイプラインを詳細に記述するパイプラインディスクリプターを定義し、それに基づいてパイプラインを生成します。ここでの基本デモには 1 個のパイプラインのみがありますが、自明でないアプリケーションは通常異なる目的のための複数のパイプラインを持ちます。
  5. 計算またはレンダリングパスの実行: これはいくつかのサブ手順からなります。
    1. 実行用に GPU に渡すコマンド一式をエンコードするコマンドエンコーダーを生成します。
    2. 計算またはレンダリングコマンドを発行するパスエンコーダーオブジェクトを生成します。
    3. 使用するパイプラインの指定、必要なデータの取得元となるバッファーの指定、(レンダーパイプラインの場合は) 行う描画操作の数の指定、などを行うコマンドを実行します。
    4. コマンドリストをファイナライズし、コマンドバッファーにカプセル化します。
    5. 論理デバイスのコマンドキューを通して、コマンドバッファーを GPU に送信します。

以下の節では、レンダーパイプラインの基本デモを解析し、必要なものを探索できるようにします。その後、コンピュートパイプラインの基本の例も解析し、レンダーパイプラインとの違いに注目します。

レンダーパイプラインの基本

レンダリング基本デモでは、 要素に青一色の背景を用意し、その上に三角形を描画します。

シェーダーモジュールの生成

ここでは以下のシェーダーコードを用います。バーテックスシェーダーステージ (@vertex ブロック) は、位置と色が格納されたデータのチャンクを受け取り、位置に基づいて頂点を配置し、色を補間し、これらのデータをフラグメントシェーダーステージに渡します。フラグメントシェーダーステージ (@fragment ブロック) は、バーテックスシェーダーステージからデータを受け取り、指定の色に基づいて頂点の色を決定します。

js
const shaders = `
struct VertexOut {
  @builtin(position) position : vec4f,
  @location(0) color : vec4f
}

@vertex
fn vertex_main(@location(0) position: vec4f,
               @location(1) color: vec4f) -> VertexOut
{
  var output : VertexOut;
  output.position = position;
  output.color = color;
  return output;
}

@fragment
fn fragment_main(fragData: VertexOut) -> @location(0) vec4f
{
  return fragData.color;
}
`;

メモ: ここでのデモではシェーダーコードをテンプレートリテラルに格納していますが、WebGPU プログラムに渡すテキストとして取得しやすい場所ならどこに格納することもできます。たとえば、シェーダーを