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<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)
uniform
storage
workgroup
private
function
handle
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) -> T
const_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++) {
}