前端工程
WebGPU Compute Shader:見落とされたGPU計算の切り札
WebGPUと聞いて多くの人がまず思うのは「WebGLの代替」です。しかし、WebGPUを単なる次世代グラフィックスAPIとして使うだけでは、大きな見落としがあります——Compute ShaderこそがWebGPUの真の切り札です。
WebGPUのCompute Shaderは、ブラウザに初めてネイティブなGPGPU能力をもたらしました。これは漸進的な改善ではなく、パラダイムシフトです。
WGSLコア構文
WebGPUはGLSLに代わる新しいシェーダー言語WGSLを導入しました:
@group(0) @binding(0) var<storage, read> inputData: array<f32>;
@group(0) @binding(1) var<storage, read_write> outputData: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
let idx = global_id.x;
if (idx >= arrayLength(&inputData)) { return; }
outputData[idx] = inputData[idx] * 2.0;
}
Workgroup Sizeの推奨値
| ハードウェア | 推奨サイズ | 理由 |
|---|---|---|
| NVIDIA | 128-256 | Warpサイズが32 |
| AMD | 64-128 | Wavefrontサイズが64 |
| Intel | 32-64 | サブグループサイズが小さい |
| 安全なデフォルト | 64 | 全ハードウェア互換 |
Compute Shader vs WebGL Transform Feedback
| 項目 | WebGL TF | WebGPU Compute |
|---|---|---|
| プログラミングモデル | レンダリングパイプラインの偽装 | ネイティブ計算 |
| データ転送 | テクスチャ/VBO | Storage Buffer |
| スレッド制御 | 頂点数によるシミュレーション | 正確なdispatch制御 |
| データ読み戻し | 遅いreadPixels | 高速なmapAsync |
| ランダム書き込み | 非対応 | アトミック操作+ランダム書き込み |
| 共有メモリ | なし | Workgroup Shared Memory |
実践:Compute Shaderによるガウシアンブラー
JavaScriptで1080p画像のガウシアンブラーを処理するには約450msかかりますが、Compute Shaderなら約5msで完了——約50倍高速です。
@group(0) @binding(0) var srcTexture: texture_2d<f32>;
@group(0) @binding(1) var dstTexture: texture_storage_2d<rgba8unorm, write>;
@group(0) @binding(2) var<uniform> params: Params;
struct Params {
width: u32,
height: u32,
radius: f32,
}
@compute @workgroup_size(16, 16)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
let pixel_coords = vec2<u32>(global_id.x, global_id.y);
if (pixel_coords.x >= params.width || pixel_coords.y >= params.height) { return; }
let sigma = params.radius / 3.0;
var color = vec4<f32>(0.0);
var total_weight = 0.0;
let radius_i = i32(params.radius);
for (var dx: i32 = -radius_i; dx <= radius_i; dx++) {
for (var dy: i32 = -radius_i; dy <= radius_i; dy++) {
let weight = exp(-(f32(dx)*f32(dx) + f32(dy)*f32(dy)) / (2.0*sigma*sigma));
let sx = clamp(i32(pixel_coords.x) + dx, 0, i32(params.width) - 1);
let sy = clamp(i32(pixel_coords.y) + dy, 0, i32(params.height) - 1);
color += textureLoad(srcTexture, vec2<u32>(sx, sy), 0) * weight;
total_weight += weight;
}
}
textureStore(dstTexture, pixel_coords, color / total_weight);
}
パフォーマンス比較
| 手法 | 1080p (r=10) | 4K (r=10) |
|---|---|---|
| JavaScript | ~450ms | ~1800ms |
| Web Worker (4スレッド) | ~130ms | ~520ms |
| WebGLフラグメント | ~12ms | ~45ms |
| WebGPU Compute | ~5ms | ~18ms |
実践:10万パーティクルシミュレーション(60fps)
10万個のパーティクルを毎フレーム更新するのはCPUでは不可能です。
struct Particle {
pos: vec2<f32>,
vel: vec2<f32>,
color: vec4<f32>,
life: f32,
}
@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
@compute @workgroup_size(256)
fn updateParticles(@builtin(global_invocation_id) global_id: vec3<u32>) {
let idx = global_id.x;
if (idx >= arrayLength(&particles)) { return; }
var p = particles[idx];
p.vel.y += -9.8 * 0.016;
p.vel *= 0.999;
p.pos += p.vel * 0.016;
if (p.pos.y < 0.0) { p.pos.y = 0.0; p.vel.y *= -0.7; }
p.life -= 0.016;
if (p.life <= 0.0) {
p.pos = vec2<f32>(0.5, 0.8);
p.life = 3.0;
}
particles[idx] = p;
}
重要なテクニック:同じバッファをSTORAGE | VERTEXとして使用し、Compute→Renderのゼロコピーを実現します。
ブラウザ互換性
| ブラウザ | サポート | バージョン |
|---|---|---|
| Chrome | ✅ 完全対応 | 113+ |
| Edge | ✅ 完全対応 | 113+ |
| Firefox | ⚠️ 実験的 | Nightly |
| Safari | ⚠️ 部分対応 | 17.4+ |
WebGPU + Web Worker連携パターン
メインスレッド (WebGPU Compute) → mapAsync → SharedArrayBuffer → Web Worker (ロジック)
GPUが重い計算を担当し、CPUがロジック判断を行うシナリオに最適です。
まとめ
WebGPU Compute ShaderはWebプラットフォームに真のGPGPUをもたらします。Chrome 113+で安定サポートされ、エコシステムも急速に成熟しています。今後はWebGPU SubgroupsとRay Tracingの提案に注目です。
ブラウザローカルツールを無料で試す →
#WebGPU#Compute Shader#GPU计算#WGSL#并行计算