WGSL 仕様メモ
WGSL
WGSL (WebGPU Shader Language) の文法や仕様の日本語解説が少ないので自分用メモ。仕様を網羅しているわけではなく、自分が書く上で必要そうな部分を適当に抜粋してまとめた。
Tour of WGSL と WGSL の仕様 から日本語に訳して簡単にまとめた。 仕様は長すぎて辛いけど困ったら見るべき。全部書いてある。
定数 (const)
const の型は abstract-int と abstract-float として扱われ、精度は実際の変数に代入する場所で決まる。
変数
let はイミュータブルな変数の宣言。ミュータブル変数は var で宣言する。
var は var<AS> で宣言する。AS はアドレス空間 (address spaces)
var<function>
関数内のローカル変数のデフォルトは var<function> であり、普通ローカル変数では単に var で宣言する。
初期値を省略した場合、ゼロ初期化される。
var<private>
シェーダーモジュール内でのグローバル変数。同一モジュール内の全ての関数からアクセス可能。 宣言時の初期値は const-expression でなければならない。省略した場合はゼロ初期化される。 モジュールグローバルでのみ宣言可能。
var<workgroup>
workgroup 内で共有されるミュータブル変数。各呼び出し (invocation) の間では atomic や workgroupBarrier で同期する必要がある。
モジュールグローバルでのみ宣言可能。
初期化式は必要なく、各ワーキンググループの実行時に自動でゼロ初期化される。
var<uniform>
ユニフォーム変数
var<storage>
(自分が使ってないので調べてない)
var<handle>
(自分が使ってないので調べてない)
let
イミュータブルな変数宣言。初期化子が必須。関数内ローカルでのみ宣言可能。 ポインタ型にすることが可能。
属性
@ から始まるのが属性。
@vertex fn vs_main() -> @builtin(position) vec4<f32> {
...
}
行列
行列の引数省略時はゼロ初期化。引数は列優先。メモリ上の表現も列優先。
const zero_init = mat3x4<f32>(); /* | 0 0 0 | | 0 0 0 | | 0 0 0 | | 0 0 0 | */ const column_wise = mat3x2<f32>(vec2<f32>(1, 2), vec2<f32>(4, 5), vec2<f32>(6, 7)); /* | 1 4 6 | | 2 5 7 | */ const scalar_wise = mat2x3<f32>(1, 2, 3, 4, 5, 6); /* | 1 4 | | 2 5 | | 3 6 | */
配列
- Fixed-Sized Array
array<T, N> - Runtime-Sized Array
array<T>
Fixed-Sized Array の配列長 N は定数式ならリテラルでなくともよい。
const numbers = array<i32, 7>(1, 4, 2, 3); const three = 3; const foo: array<i32, three * 2 + 1> = numbers;
Runtime-Sized Array は Storage Buffer のリソースとしてのみ使える。 バッファ全体を表すか、バッファ全体を表す構造体の最後のメンバーとして使う。
要素数は実行時に決定され、変数に関連する BufferBinding のサイズに収まる範囲で可能な限り大きくなる。
実行時の配列長は arrayLength で取得できる。
インデックスによって値を取得できるが、配列自体を変数に代入することはできない。
@group(0) @binding(0) var<storage> weights: array<f32>;
struct PointLight { position: vec3f, color: vec3f, }
struct LightData {
meanIntensity: f32,
point: array<PointLight>,
}
@group(0) @binding(1) var<storage> lights: LightData;
fn number_of_lights() -> u32 {
return arrayLength(&lights.point);
}
fn get_point_light(i: i32) -> PointLight {
return lights.point[i];
}
fn cannot_copy_whole_array() {
// 通常の変数に代入はできない
// var all_point_lights = lights.point; // Error
}
構造体 (struct)
struct Data {
a: f32,
b: f32,
}
constructible な構造体の場合、() で初期化できる。
constructible とは
- スカラー型 (
u32,f32, etc...) - ベクトル型 (
vec2<f32>,vec3<f32>, etc...) - 行列型 (
mat2x2<f32>,mat3x3<f32>, etc...) - 要素の型が constructible な固定長配列型 (
array<f32, 3>, etc...) - メンバーの型が全て constructible な構造体型
const data = Data(1.0, 2.0);
原子性 (atomicity)
uniform, storage, workgroup は並列実行において共有されている。
uniform は読み取り専用なので問題は起こらないが、storage と workgroup は書き込みも可能であるため、競合に注意する必要がある。
atomic<T> は i32 と u32 に対してのみ使える。
atomic<T> は workgroup と storage のアドレス空間の変数でのみ使える。
var<workgroup> counter: atomic<u32>;
atomic<T> は constructible ではないため、以下の状況では直接使用できない。
- expression に書けない
- 関数の引数、戻り値に書けない
- 変数に代入できない
- 変数の初期化式に書けない
ビルトイン関数でのみ操作可能で、atomicLoad と atomicStore で読み書きできる。
他にも atomicAdd, atomicMin, atomicMax, atomicAnd, atomicOr, atomicXor などがある。
fn atomicLoad(atomic_ptr: ptr<AS, atomic<T>, read_write>) -> T fn atomicStore(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T)
ポインタ (pointer)
ポインタは ptr<AS, T, AM> で表される。
ASはアドレス空間 (address spaces)uniformstorageworkgroupprivatefunctionhandle
Tはポインターが指す型AMはアクセスモード。省略可能read(default)read_write
アロー演算子 a->b はない。カッコで囲んで (*a).b のように書く。
ユーザー定義関数の引数にポインタを使う時の制約
- アドレス空間は
functionかprivateでなければならない - 変数全体へのポインタのみ使える。複合変数の一部のみのアドレスを渡すことはできない
struct Data {
aaa: u32,
bbb: u32,
}
var<private> data: Data;
fn foo() {
//bar(&data.aaa); // Error: 複合変数の一部のみのアドレスを渡すことはできない
}
式 (expression)
三つの評価フェーズがある。
- Shader creation time
- Pipeline creation time
- Shader execution time
Shader creation time
const-expressions の値を確定します。
- リテラル
@constな関数const宣言された値
Pipeline creation time
override-expressions の値を確定します。
override宣言された値GPUProgrammableStage.constantsからの値
Shader execution time
runtime-expressions の値を確定します。
letやvar宣言された値- 全ての呼び出し関数
- 変数値
- 変数への参照やポインタ
リテラル
サフィックスのないリテラルは abstract-int か abstract-float になり、具体的な型付きの変数に束縛される時に型が決まる。
| サフィックス | 型 | 例 |
|---|---|---|
u |
u32 |
1u |
i |
i32 |
1i |
f |
f32 |
1.0f |
@const ビルトイン関数
radians(x: T) -> Tconst_assert(x: bool)
const x = 10; const_assert(x == 10); // false ならコンパイルエラー
制御フロー
if 文
if (condition) { ... }
または
if condition { ... }
switch 文
switch (condition) { ... }
または
switch condition { ... }
let a = 4;
switch a {
case 1, 2, 3: {
}
default: {
}
}
for 文
for(var i = 0; i < 10; i++) {
}