WebGPUとは
WebGPUは、Webブラウザから低レベルのGPU機能にアクセスするための次世代APIです。WebGLの後継として設計され、Vulkan、Metal、Direct3D 12などのモダンなグラフィックスAPIを抽象化しています。
flowchart TB
subgraph Web["Webアプリケーション"]
App["JavaScript/TypeScript"]
end
subgraph API["グラフィックスAPI"]
WebGPU["WebGPU API"]
end
subgraph Native["ネイティブAPI"]
Vulkan["Vulkan<br/>(Linux/Android)"]
Metal["Metal<br/>(macOS/iOS)"]
D3D12["Direct3D 12<br/>(Windows)"]
end
subgraph GPU["GPU"]
Hardware["GPUハードウェア"]
end
App --> WebGPU
WebGPU --> Vulkan
WebGPU --> Metal
WebGPU --> D3D12
Vulkan --> Hardware
Metal --> Hardware
D3D12 --> Hardware
WebGL vs WebGPU
| 特徴 | WebGL 2.0 | WebGPU |
|---|---|---|
| 設計年 | 2017 | 2023 |
| ベース | OpenGL ES 3.0 | Vulkan/Metal/D3D12 |
| コンピュートシェーダー | × | ◎ |
| マルチスレッド | × | ◎ |
| メモリ管理 | 自動 | 明示的 |
| バインドグループ | × | ◎ |
| シェーダー言語 | GLSL | WGSL |
基本的な使い方
1. GPUアダプターの取得
async function initWebGPU(): Promise<{ device: GPUDevice; context: GPUCanvasContext }> {
// GPUアダプターを要求
const adapter = await navigator.gpu?.requestAdapter();
if (!adapter) {
throw new Error('WebGPU not supported');
}
// GPUデバイスを取得
const device = await adapter.requestDevice();
// キャンバスコンテキストを設定
const canvas = document.querySelector('canvas')!;
const context = canvas.getContext('webgpu')!;
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format,
alphaMode: 'premultiplied',
});
return { device, context };
}
2. シェーダーの作成(WGSL)
// 頂点シェーダー
@vertex
fn vertexMain(@location(0) position: vec3f) -> @builtin(position) vec4f {
return vec4f(position, 1.0);
}
// フラグメントシェーダー
@fragment
fn fragmentMain() -> @location(0) vec4f {
return vec4f(1.0, 0.5, 0.2, 1.0); // オレンジ色
}
3. レンダリングパイプライン
function createPipeline(device: GPUDevice, format: GPUTextureFormat): GPURenderPipeline {
const shaderModule = device.createShaderModule({
code: `
@vertex
fn vertexMain(@location(0) position: vec3f) -> @builtin(position) vec4f {
return vec4f(position, 1.0);
}
@fragment
fn fragmentMain() -> @location(0) vec4f {
return vec4f(1.0, 0.5, 0.2, 1.0);
}
`,
});
return device.createRenderPipeline({
layout: 'auto',
vertex: {
module: shaderModule,
entryPoint: 'vertexMain',
buffers: [{
arrayStride: 12, // 3 floats * 4 bytes
attributes: [{
format: 'float32x3',
offset: 0,
shaderLocation: 0,
}],
}],
},
fragment: {
module: shaderModule,
entryPoint: 'fragmentMain',
targets: [{ format }],
},
});
}
コンピュートシェーダー(GPGPU)
WebGPUの最大の特徴は、グラフィックス以外の汎用計算(GPGPU)が可能なことです。
行列演算の例
async function matrixMultiply(device: GPUDevice, a: Float32Array, b: Float32Array): Promise<Float32Array> {
const size = Math.sqrt(a.length);
// バッファ作成
const bufferA = device.createBuffer({
size: a.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
const bufferB = device.createBuffer({
size: b.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
const bufferResult = device.createBuffer({
size: a.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
device.queue.writeBuffer(bufferA, 0, a);
device.queue.writeBuffer(bufferB, 0, b);
// コンピュートシェーダー
const shaderModule = device.createShaderModule({
code: `
@group(0) @binding(0) var<storage, read> matrixA: array<f32>;
@group(0) @binding(1) var<storage, read> matrixB: array<f32>;
@group(0) @binding(2) var<storage, read_write> result: array<f32>;
@compute @workgroup_size(8, 8)
fn main(@builtin(global_invocation_id) id: vec3u) {
let row = id.x;
let col = id.y;
let size = ${size}u;
if (row >= size || col >= size) { return; }
var sum = 0.0;
for (var i = 0u; i < size; i++) {
sum += matrixA[row * size + i] * matrixB[i * size + col];
}
result[row * size + col] = sum;
}
`,
});
// パイプライン作成
const pipeline = device.createComputePipeline({
layout: 'auto',
compute: {
module: shaderModule,
entryPoint: 'main',
},
});
// バインドグループ
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: bufferA } },
{ binding: 1, resource: { buffer: bufferB } },
{ binding: 2, resource: { buffer: bufferResult } },
],
});
// 実行
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginComputePass();
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatchWorkgroups(Math.ceil(size / 8), Math.ceil(size / 8));
passEncoder.end();
// 結果を読み取り
const readBuffer = device.createBuffer({
size: a.byteLength,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
});
commandEncoder.copyBufferToBuffer(bufferResult, 0, readBuffer, 0, a.byteLength);
device.queue.submit([commandEncoder.finish()]);
await readBuffer.mapAsync(GPUMapMode.READ);
const result = new Float32Array(readBuffer.getMappedRange().slice(0));
readBuffer.unmap();
return result;
}
WGSL(WebGPU Shading Language)
WebGPU専用のシェーダー言語で、Rustに似た構文を持ちます。
基本構文
// 変数宣言
var<private> count: u32 = 0;
let constant: f32 = 3.14159;
// 構造体
struct Vertex {
@location(0) position: vec3f,
@location(1) color: vec4f,
}
// 関数
fn add(a: f32, b: f32) -> f32 {
return a + b;
}
// 組み込み関数
fn example() {
let v = vec3f(1.0, 2.0, 3.0);
let len = length(v); // ベクトル長
let n = normalize(v); // 正規化
let d = dot(v, n); // 内積
let c = cross(v, n); // 外積
let m = max(1.0, 2.0); // 最大値
let s = sin(3.14 / 2.0); // 三角関数
}
ユースケース
1. 機械学習推論
// ブラウザ上でのニューラルネットワーク推論
import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-webgpu';
await tf.setBackend('webgpu');
const model = await tf.loadGraphModel('model.json');
const prediction = model.predict(inputTensor);
2. 画像・動画処理
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
@group(0) @binding(1) var outputTexture: texture_storage_2d<rgba8unorm, write>;
@compute @workgroup_size(8, 8)
fn blur(@builtin(global_invocation_id) id: vec3u) {
// ガウシアンブラー処理
var sum = vec4f(0.0);
let weights = array<f32, 9>(
0.0625, 0.125, 0.0625,
0.125, 0.25, 0.125,
0.0625, 0.125, 0.0625
);
for (var dy = -1; dy <= 1; dy++) {
for (var dx = -1; dx <= 1; dx++) {
let coord = vec2i(id.xy) + vec2i(dx, dy);
let color = textureLoad(inputTexture, coord, 0);
sum += color * weights[(dy + 1) * 3 + (dx + 1)];
}
}
textureStore(outputTexture, vec2i(id.xy), sum);
}
3. 物理シミュレーション
struct Particle {
position: vec3f,
velocity: vec3f,
}
@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
@compute @workgroup_size(64)
fn simulate(@builtin(global_invocation_id) id: vec3u) {
let index = id.x;
var p = particles[index];
// 重力
p.velocity.y -= 9.8 * deltaTime;
// 位置更新
p.position += p.velocity * deltaTime;
// 地面との衝突
if (p.position.y < 0.0) {
p.position.y = 0.0;
p.velocity.y *= -0.8; // 反発係数
}
particles[index] = p;
}
ブラウザサポート
| ブラウザ | サポート状況 |
|---|---|
| Chrome 113+ | ◎ 完全サポート |
| Edge 113+ | ◎ 完全サポート |
| Firefox | △ フラグで有効化 |
| Safari 17+ | ○ サポート |
関連記事
- WebAssembly - Wasm基礎
- パフォーマンス最適化 - 最適化手法
- 非同期プログラミング - 非同期処理
まとめ
WebGPUは、Webプラットフォームに革新的なGPU機能をもたらします。
- モダンAPI: Vulkan/Metal/D3D12の抽象化
- コンピュートシェーダー: GPGPU対応
- WGSL: 安全で表現力豊かなシェーダー言語
- 高性能: 低オーバーヘッドな設計
3Dグラフィックス、機械学習、科学計算など、GPU性能を必要とするWebアプリケーションの可能性を大きく広げます。
← 一覧に戻る