この記事はWebGL Advent Calendar 2019の1日目の記事です。
内容が古くなっているので、最新はこちら(WGSL版)を参照ください。
はじめに
WebGPU
って何なの?長いコード見てる時間ないよ、という忙しい人向けに、贅肉をそぎ落とした簡易版のコードを書いてみました。
ここ最近、Windows 版の Chrome Canary
でも WebGPU
が試せるようになってきたので興味がある方はこの機会にお試しください。
WebGPU とは
WebGPU
は WebGL
や WebGL2
の後継とされているグラフィックス API です。
WebGL
は Khronos グループが中心となって仕様策定が行われていましたが WebGPU
は W3C コミュニティグループにより行われています。
WebGPU
は、より低レベルな GPU アクセスを目的とした API の為、WebGL
に比べパフォーマンスの向上が期待されています。
ブラウザのAPI | 対応するバックエンドのAPI | 利用可能なシェーダ | 仕様策定 |
---|---|---|---|
WebGL 1.0 | OpenGL ES 2.0 | GLSL ES 1.0 | Khronos |
WebGL 2.0 | OpenGL ES 3.0 | GLSL ES 3.0 | Khronos |
WebGPU | Vulkan/DirectX/Metal | SPIR-V/WHLSL (※) | W3C |
※ WebGPU のシェーダは未決定な部分が多く今後変更が入る可能性がある為、ご注意ください。
SPIR-V / WHLSL とは
SPIR-V
は Khronos グループが提案する計算/グラフィックス用のシェーダ中間言語です。GLSL
や HLSL
をコンパイルした際の中間形式(バイナリ)になります。
Chrome
や Firefox
では WebGPU のシェーダとして SPIR-V
を採用しています。
WHLSL
は Apple が提案するシェーダ言語です。
WebGPU の有効化方法
WebGPU は Chrome Canary
や Safari Technology Preview
などで有効化することで試すことができます。
これらの機能は、実験的な機能である為、有効化したまま、信頼できない Web へのアクセスは行わないでください。
ブラウザ | 対応するバックエンドのライブラリ | WebGPU 有効化方法 |
---|---|---|
Chrome Canary | Dawn | chrome://flags/#enable-unsafe-web |
Firefox Nightly | gfx-rs | dom.webgpu.enable |
Safari Technology Preview | webkit | Develop -> Experimental Features -> WebGPU |
ポリゴン1枚を描画する WebGPU コード
お待たせしました。ようやくコードです。
とりあえずポリゴン1枚を描画する WebGPU
コードとなっています。WebGL
に比べて2~3倍のコード量でしょうか。
ちなみに、現状、このサンプルは Chrome Canary
でしか動作しないのでご注意ください。
const ready = glslang();
ready.then(init);
const vs = `#version 450
layout(location = 0) in vec3 position;
void main() {
gl_Position = vec4(position, 1.0);
}`;
const fs = `#version 450
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(0.0, 0.0, 1.0, 1.0);
}`;
async function init(glslang) {
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const c = document.getElementById('c');
const ctx = c.getContext('gpupresent');
const swapChainFormat = "bgra8unorm";
const swapChain = ctx.configureSwapChain({device, format: swapChainFormat});
const pipeline = device.createRenderPipeline({
layout: device.createPipelineLayout({bindGroupLayouts: []}),
vertexStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(vs, 'vertex'),
source: vs
}),
entryPoint: 'main'
},
fragmentStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(fs, 'fragment'),
source: fs
}),
entryPoint: 'main'
},
vertexState: {
indexFormat: 'uint32',
vertexBuffers: [{
arrayStride: 12, // 3 * 4bytes
attributes: [{
shaderLocation: 0,
offset: 0,
format: "float3"
}]
}]
},
colorStates: [{
format: swapChainFormat,
alphaBlend: {
srcFactor: "src-alpha",
dstFactor: "one-minus-src-alpha",
operation: "add"
}
}],
primitiveTopology: 'triangle-list',
});
const positions = [
0.0, 0.5, 0.0, // v0
-0.5,-0.5, 0.0, // v1
0.5,-0.5, 0.0 // v2
];
const data = new Float32Array(positions);
const vertexBuffer = device.createBuffer({
size: data.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
vertexBuffer.setSubData(0, data);
const render = function () {
const commandEncoder = device.createCommandEncoder();
const textureView = swapChain.getCurrentTexture().createView();
const renderPassDescriptor = {
colorAttachments: [{
attachment: textureView,
loadValue: {r: 1.0, g: 1.0, b: 1.0, a: 1.0},
}]
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setVertexBuffer(0, vertexBuffer);
passEncoder.setPipeline(pipeline);
passEncoder.draw(3, 1, 0, 0);
passEncoder.endPass();
device.defaultQueue.submit([commandEncoder.finish()]);
}
requestAnimationFrame(render);
}
コードのダウンロード
実際に動作するコードは jsfiddle にアップしてありますので、ご利用ください。
[jsfiddle] 100行で WebGPU 試してみるテスト ※ 実行には WebGPU 対応ブラウザが必要です。
参考までに、WebGL の最小サンプルは、下記を参照ください。
[Qiita] 30行で WebGL を試してみるテスト
あとがき
WebGPU
は、ようやく少し試せるようになってきましたが、まだまだ実験段階で、昨日書いたサンプルが今日動かないということも多々ある状況です。安定性重視な方は、もう少し待った方が良いかと思います。
セキュリティ面やシェーダ周りの仕様など、課題が山積しているようなので、通常のブラウザ環境で利用できるようになるには、あと1~2年はかかるのではないでしょうか。
WebGPU
の動向が知りたい方は、以下の Wiki が更新されているので参照されると良いと思います。
■ Implementation Status
https://github.com/gpuweb/gpuweb/wiki/Implementation-Status