この記事はWeb グラフィックス Advent Calendar 2020の13日目の記事です。
※ 2023年1月7日、内容が古くなっていた為、記事の内容を最新化しました。
変更箇所は「主な変更内容(2020年12月~2022年12月)」を参照ください。
はじめに
WebGPU
は WebGL
や WebGL2
の後継とされているグラフィックス API です。
今のところリリース時期としては 2023年5月 Chrome 113 を目標 としているようです。
API の仕様は固まりつつありますが、まだ仕様変更等が入る可能性があることにご注意ください。
ここでは、主に、シェーダ機能「WGSL」(WebGPU Shading Language)について試してみようと思います。
2019年時点は WebGPU 用のシェーダの方針が決まっていなかった為、glslang
と呼ばれるライブラリを用いて GLSL
→ SPIR-V
(シェーダ中間言語)にコンパイルして使用する形が取られていました。
現在は、Chrome Canary
で新しいシェーダ WGSL
が実験的に使えるようになってきた為、それを使ってみようと思います。
WGSL とは
WGSL
は WebGPU Shading Language
の略で、その名の通り WebGPU
用のシェーダです。
GLSL
では C 言語ライクな構文でしたが WGSL
では Rust に似た構文が採用されています。
2019年から2022年にかけて何回か構文の仕様変更がありました。
以下のサンプルコードは現時点の最新の仕様の内容で更新しています。
GLSL と WGSL のコードの比較
箇所 | GLSL | WGSL |
---|---|---|
VSコード | ||
FSコード |
WebGPU の有効化方法
WebGPU は Chrome Canary
や Firefox Nightly
でフラグを有効化することで試すことができます。
ただし 2023年1月時点では、WGSL
は Chrome Canary
でしか動作しないようです。
なお、これらの機能は、実験的な機能である為、有効化したまま、信頼できない 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 コード
以下、シェーダに WGSL
を使用した場合のサンプルコードです。動作確認は Chrome Canary 111
で行いました。他の環境では動作しない可能性がありますのでご注意ください。
<canvas id="c" width="512" height="512"></canvas>
<script id="vs" type="x-shader/x-vertex">
struct VertexOutput {
@builtin(position) Position : vec4<f32>
}
@vertex
fn main(
@location(0) position : vec3<f32>
) -> VertexOutput {
var output : VertexOutput;
output.Position = vec4<f32>(position, 1.0);
return output;
}
</script>
<script id="fs" type="x-shader/x-fragment">
@fragment
fn main() -> @location(0) vec4<f32> {
return vec4<f32>(0.0, 0.0, 1.0, 1.0);
}
</script>
const vs = document.getElementById("vs").textContent;
const fs = document.getElementById("fs").textContent;
init();
async function init() {
const gpu = navigator["gpu"];
const adapter = await gpu.requestAdapter();
const device = await adapter.requestDevice();
const c = document.getElementById("c");
c.width = window.innerWidth;
c.height = window.innerHeight;
const ctx = c.getContext("webgpu");
const format = gpu.getPreferredCanvasFormat();
ctx.configure({
device: device,
format: format,
alphaMode: "opaque"
});
const pipeline = device.createRenderPipeline({
layout: "auto",
vertex: {
module: device.createShaderModule({
code: vs
}),
entryPoint: "main",
buffers: [
{
arrayStride: 3 * 4,
attributes: [
{
// position
shaderLocation: 0,
offset: 0,
format: "float32x3"
}
]
}
]
},
fragment: {
module: device.createShaderModule({
code: fs
}),
entryPoint: "main",
targets: [
{
format: format
}
]
},
primitive: {
topology: "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
];
let vertexBuffer = makeVertexBuffer(device, new Float32Array(positions));
const render = function () {
const commandEncoder = device.createCommandEncoder();
const textureView = ctx.getCurrentTexture().createView();
const renderPassDescriptor = {
colorAttachments: [{
view: textureView,
loadOp: "clear",
clearValue: {r: 1, g: 1, b: 1, a: 1},
storeOp: "store"
}]
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.setVertexBuffer(0, vertexBuffer);
passEncoder.draw(3, 1, 0, 0);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
}
requestAnimationFrame(render);
}
function makeVertexBuffer(device, data) {
const verticesBuffer = device.createBuffer({
size: data.byteLength,
usage: GPUBufferUsage.VERTEX,
mappedAtCreation: true,
});
new Float32Array(verticesBuffer.getMappedRange()).set(data);
verticesBuffer.unmap();
return verticesBuffer;
}
実際に動作するコードは jsfiddle にアップしてありますので、ご利用ください。 ※ 実行には WebGPU 対応ブラウザが必要です。
[jsfiddle] 100行で WebGPU 試してみるテスト(GLSL版)
[jsfiddle] 100行で WebGPU 試してみるテスト(WGSL版)
主な変更内容(2020年12月~2022年12月)
■ WGSL 部分
変更内容:
・GLSL
で言うところの attribute
属性はシェーダの main
関数の引き数で受け取れるようになりました。
・struct
(構造体)が指定できるようになりました。
・属性を指定する書き方が Java のアノテーションのような書き方になりました。
例)
変更前 | 変更後 |
---|---|
[[location(0)]] |
@location(0) |
[[builtin(position)]] |
@builtin(position) |
[[stage(vertex)]] |
@vertex |
[[stage(fragment)]] |
@fragment |
■ WebGPU API 部分
変更内容:
・コンテキストを取得する際の名称が gpupresent
→webgpu
に変更されました。
・ピクセルフォーマットが gpu.getPreferredCanvasFormat()
で取得できるようになりました。
・Render Pipline の設定で colorStates:
の指定が不要になりました。
・Render Pass の colorAttachments:
の指定方法が変更になりました。loadOpe
/ storeOp
の指定が必要になりました。
・冗長だった名称が簡素化されました。
例)
変更前 | 変更後 |
---|---|
vertexStage: |
vertex: |
fragmentStage: |
fragment: |
colorStates: |
廃止 |
vertexBuffers: |
buffers: |
primitiveTopology: |
primitive: |
frontFrace: |
primitive: に移動 |
cullMode: |
primitive: に移動 |
context.configureSwapChain() |
context.configure() |
passEncoder.endPass() |
passEncoder.end() |
device.defaultQueue |
device.queue |
その他のサンプル
手前味噌ですが、いくつかの基本的なサンプルについて GLSL 版と WGSL 版のサンプルを下記に置いてありますので興味ある方はどうぞ。
おわりに
何故シェーダ言語が GLSL
ベースではなく Rust
ベースの新しい言語になったかは、少し闇が深そうなので、触れないでおきます。。
API やシェーダの仕様は固まりつつあるので、WebGPU
の新しいシェーダ言語 WGSL
に今のうちから慣れておくのも良いのではないでしょうか。
参考情報
■ WebGPU 仕様
https://www.w3.org/TR/webgpu/
■ WGSL 仕様
https://www.w3.org/TR/WGSL/
■ WebGPU Samples (WebGPU サンプル)
https://github.com/austinEng/webgpu-samples
■ WebGPU - Chrome Developers(開発ステータス)
https://developer.chrome.com/docs/web-platform/webgpu/
■ Raw WebGPU (WebGPU のチュートリアル)
https://alain.xyz/blog/raw-webgpu