1. はじめに
WebGPUシェーディング言語(WGSL)は、[WebGPU]のためのシェーダー言語です。 つまり、WebGPU APIを使用するアプリケーションは、GPU上で実行されるプログラム(シェーダー)をWGSLで記述します。
// テクスチャ付きジオメトリを点光源で照らすフラグメントシェーダー。 // ストレージバッファバインディングからのライト。 struct PointLight { position: vec3f, color : vec3f, } struct LightStorage { pointCount : u32, point : array< PointLight > , } @group ( 0 ) @binding ( 0 ) var < storage> lights : LightStorage ; // テクスチャとサンプラー。 @group ( 1 ) @binding ( 0 ) var baseColorSampler : sampler; @group ( 1 ) @binding ( 1 ) var baseColorTexture : texture_2d< f32> ; // 関数の引数は頂点シェーダーから渡される値。 @fragment fn fragmentMain ( @location ( 0 ) worldPos : vec3f, @location ( 1 ) normal : vec3f, @location ( 2 ) uv : vec2f) -> @location ( 0 ) vec4f{ // テクスチャから表面の基本色をサンプルする。 let baseColor = textureSample ( baseColorTexture , baseColorSampler , uv ); let N = normalize ( normal ); var surfaceColor = vec3f( 0 ); // シーン内の点光源をループ処理。 for ( var i = 0u ; i < lights . pointCount ; i ++ ) { let worldToLight = lights . point [ i ]. position- worldPos ; let dist = length ( worldToLight ); let dir = normalize ( worldToLight ); // このライトの表面色への寄与を算出。 let radiance = lights . point [ i ]. color * ( 1 / pow ( dist , 2 )); let nDotL = max ( dot ( N , dir ), 0 ); // 表面色にライトの寄与を加算。 surfaceColor += baseColor . rgb * radiance * nDotL ; } // 加算した表面色を返す。 return vec4( surfaceColor , baseColor . a ); }
1.1. 概要
WebGPUは、GPUコマンドという形で作業単位をGPUに発行します。 WGSLは、2種類のGPUコマンドに関係します:
-
描画コマンドは、レンダーパイプラインを 入力、出力、および接続されたリソースのコンテキストで実行します。
-
ディスパッチコマンドは、コンピュートパイプラインを 入力と接続されたリソースのコンテキストで実行します。
両方の種類のパイプラインで、WGSLで記述されたシェーダーが使われます。
シェーダーは、 WGSLプログラム内でパイプラインのシェーダーステージを実行する部分です。 シェーダーは次の要素で構成されます:
-
エントリーポイントから開始して呼び出される全関数の推移的閉包。 この集合にはユーザー定義関数と組み込み関数の両方が含まれます。 (より厳密な定義は「シェーダーステージ内の関数」を参照してください。)
-
それらすべての関数によって静的にアクセスされる変数および定数の集合。
-
それらすべての関数、変数、定数を定義または解析するために使われる型の集合。
注意: WGSLプログラムはエントリーポイントを必須としません。ただし、エントリーポイントがないプログラムはAPIによって実行できません。なぜなら、エントリーポイントがGPUProgrammableStage
の作成に必要だからです。
シェーダーステージを実行する際、実装は次の処理を行います:
-
モジュールスコープで宣言された定数の値を計算します。
-
シェーダーのリソースインターフェース内の変数にリソースをバインドし、 実行中にそれらのリソース内容を利用できるようにします。
-
その他のモジュールスコープ変数のためのメモリを割り当て、 指定された初期値でそのメモリを初期化します。
-
エントリーポイントに形式的パラメータがある場合、シェーダーステージの入力値でそれらを初期化します。
-
エントリーポイントの返り値がある場合、それをシェーダーステージの出力に接続します。
-
その後、エントリーポイントを呼び出します。
WGSLプログラムは次の要素から構成されます:
-
ディレクティブ(モジュールレベルの振る舞い制御)。
-
関数(実行の振る舞いを定義)。
-
文(宣言または実行単位)。
-
リテラル(純粋な数値を表現するテキスト)。
-
定数(特定のタイミングで計算された値に名前を付与)。
-
変数(値を保持するメモリに名前を付与)。
-
式(値を組み合わせて結果値を生成)。
-
型(以下を記述):
-
値の集合。
-
対応する式の制約。
-
それらの式の意味論。
-
-
属性(以下のような追加情報を指定):
注意: WGSLプログラムは現在、単一のWGSLモジュールで構成されます。
WGSLは命令型言語です。挙動は実行する文の列として記述します。 文は次のことができます:
-
変数の内容を変更する。
-
構造化プログラミング構造を使って実行順序を制御する:
-
上記の挙動の一部として値を計算するために式を評価する。
WGSLは静的型付け言語です。各式で計算される値は、プログラムソースのみから決定される特定の型を持ちます。
WGSLにはブール値や数値(整数と浮動小数点)を記述する型があります。 これらの型は複合型(ベクトル、行列、 配列、構造体)に集約できます。 WGSLには、固有の操作を提供するアトミック型などの特殊な型があります。 WGSLはメモリに格納可能な型をメモリビューとして記述します。 WGSLはテクスチャやサンプラーなど、よく使われるレンダリング型も提供します。 これらの型には、グラフィックスレンダリングのためのGPUハードウェアを活用する組み込み関数が用意されています。
WGSLには、具体型からの暗黙の変換や昇格はありませんが、 抽象型からの暗黙の変換・昇格は許可されます。 ある具体型の数値型やブール型から他の型へ値を変換するには、 明示的な変換や 値コンストラクタ、 ビットの再解釈が必要です。ただし、WGSLはスカラー型からベクトル型への限定的な昇格機能を提供します。 これは複合型にも適用されます。
シェーダーステージの作業は、1つ以上の呼び出しに分割されます。 各呼び出しはエントリーポイントを実行しますが、状況が少し異なります。 シェーダーステージの呼び出しは、特定の変数へのアクセスを共有します:
-
ステージ内のすべての呼び出しは、シェーダーインターフェースのリソースを共有します。
-
コンピュートシェーダーでは、同じ ワークグループ内の呼び出しは、 workgroup アドレス空間の変数を共有します。 異なるワークグループ間ではそれらの変数は共有されません。
ただし、呼び出しはステージ入力(同僚と区別する識別値を提供する組み込み入力を含む)の異なる集合に対して動作します。 各呼び出しは、privateやfunctionアドレス空間の変数として独立したメモリ空間を持ちます。
同じシェーダーステージ内の呼び出しは同時に実行され、多くの場合並列で動作します。 シェーダー作成者は、呼び出しの動的な挙動が次を満たすよう責任を持ちます:
-
テクスチャサンプリングや制御バリアなど、一部の基本操作の均一性要件を満たす。
-
共有変数への競合するアクセスが発生する場合、データ競合を回避するように調整する。
WGSLは、ある機能に対して複数の挙動を許容する場合があります。 これは移植性の問題となり、実装によって異なる挙動が現れることがあります。 WGSLの設計はそのようなケースを最小化することを目指していますが、実現可能性や幅広いデバイスで高性能を発揮するという目標に制約されることもあります。
挙動要件は、 WGSLプログラムの処理や実行時に実装が行うべきアクションです。これらは プログラマーとの契約における実装の義務を示します。 仕様書は、明らかでない場合に明示的にこれらの義務を記載します。
1.2. 構文記法
以下の構文記法はWGSLの構文文法規則の慣例を示します:
-
規則の両端が斜体の場合は構文規則を示します。
-
右辺の始まりと終わりが単一引用符(')で囲まれた太字等幅テキストはキーワードやトークンを示します。
-
普通のテキストのコロン(:)は構文規則の定義を示します。
-
普通のテキストの縦棒(|)は選択肢(オルタナティブ)を示します。
-
普通のテキストの疑問符(?)は、直前のキーワード・トークン・規則・グループが0回または1回現れる(省略可能)ことを示します。
-
普通のテキストのアスタリスク(*)は、直前のキーワード・トークン・規則・グループが0回以上現れることを示します。
-
普通のテキストのプラス(+)は、直前のキーワード・トークン・規則・グループが1回以上現れることを示します。
-
普通のテキストの丸括弧(()())は要素のグループを示します。
1.3. 数学的用語と記法
角度:
-
慣例として、角度はラジアンで計測されます。
-
角度測定の基準光線は、原点 (0,0) から (+∞,0) 方向への光線です。
-
θ を比較光線と基準光線のなす角度とします。 比較光線が反時計回りに動くと θ は増加します。
-
1周(円)は 2π ラジアンです。
-
例:
-
角度0は原点から右 (1,0) 方向
-
角度2πも原点から右 (1,0) 方向
-
角度π/4は原点から (1,1) 方向
-
角度π/2は原点から (0,1) 方向
-
角度πは原点から (-1,0) 方向
-
角度(3/2)πは原点から (0,-1) 方向
-
双曲線角は、従来の角度ではなく単位のない面積です。具体的には:
-
x2 - y2 = 1 かつ x > 0 の双曲線を考えます。
-
R を原点から双曲線上の点 (x, y) への光線とします。
-
a を、R、x軸、双曲線の曲線で囲まれる面積の2倍とします。
-
R がx軸より上なら a を正、下なら負とします。
この面積 a は双曲線角であり、x は a の双曲線余弦、y は双曲線正弦となります。
正の無限大(+∞)はすべての実数より大きい固有の値です。
負の無限大(−∞)はすべての実数より小さい固有の値です。
拡張実数(またはアフィン拡張実数)は実数全体に+∞と−∞を加えた集合です。 コンピューターは浮動小数点型で拡張実数を近似表現し、両方の無限大値も含みます。 § 15.7 浮動小数点評価も参照してください。
区間は下限と上限を持つ連続した数値集合です。 文脈によって、整数・浮動小数点・実数・拡張実数集合の場合があります。
-
閉区間 [a,b] は a ≤ x ≤ b を満たす x の集合です。
-
半開区間 [a,b) は a ≤ x < b の x集合です。
-
半開区間 (a,b] は a < x ≤ b の x集合です。
床関数は、拡張実数 x に対し:
-
⌊ +∞ ⌋ = +∞
-
⌊ −∞ ⌋ = −∞
-
実数 x について、⌊x⌋ = k(k ≤ x < k+1 となる唯一の整数 k)
天井関数は、拡張実数 x に対して:
-
⌈ +∞ ⌉ = +∞
-
⌈ −∞ ⌉ = −∞
-
実数 x について、⌈x⌉ = k(k-1 < x ≤ k となる唯一の整数 k)
切り捨て関数は、拡張実数 x に対して:
-
truncate(+∞) = +∞
-
truncate(−∞) = −∞
-
実数 x について、絶対値が x 以下となる最近接の整数値:
-
x ≥ 0 の場合 truncate(x) = ⌊x⌋、x < 0 の場合 truncate(x) = ⌈x⌉
-
roundUp関数は、正の整数 k と n に対し:
-
roundUp(k, n) = ⌈n ÷ k⌉ × k
転置は、c列 r行の行列 Aを、r列 c行の行列 ATとして Aの行を ATの列へコピーすることで作られます:
-
transpose(A) = AT
-
transpose(A)i,j = Aj,i
列ベクトルの転置は1行の行列として解釈され、同様に行ベクトルの転置は1列の行列として解釈されます。
2. WGSLモジュール
WGSLプログラムは単一のWGSLモジュールから構成されます。
モジュールは、オプションのディレクティブの並びの後に、モジュールスコープの宣言およびアサーションが続きます。 モジュールは以下の要素で構成されます:
-
ディレクティブ(モジュールレベルの振る舞い制御を指定)。
-
関数(実行時の挙動を指定)。
-
文(宣言や実行単位)。
-
リテラル(純粋な数値を表現するテキスト)。
-
変数(値を保持するメモリに名前を付与)。
-
定数(特定のタイミングで計算された値に名前を付与)。
-
式(値を組み合わせて結果値を生成)。
-
型(以下を記述):
-
値の集合。
-
対応する式の制約。
-
それらの式の意味。
-
-
属性(以下のような追加情報を指定):
global_directive * ( global_decl | global_assert | ';'
) *
2.1. シェーダーのライフサイクル
WGSLプログラムとその中のシェーダーが経験するライフサイクルには、4つの主要なイベントがあります。 最初の2つはWGSLプログラムを実行準備するために使われるWebGPU APIメソッドに対応しています。 最後の2つはシェーダーの実行開始と終了です。
イベントは以下の通りです:
-
シェーダーモジュール作成
-
これはWebGPU
createShaderModule()
メソッドが呼ばれたときに発生します。 WGSLプログラムのソーステキストはこの時点で提供されます。
-
-
パイプライン作成
-
これはWebGPU
createComputePipeline()
メソッド またはWebGPUcreateRenderPipeline()
メソッドが呼ばれたときに発生します。 これらのメソッドは、事前に作成された1つ以上のシェーダーモジュールと他の設定情報を使用します。 -
指定されたエントリーポイントの
GPUProgrammableStage
のシェーダーを構成するコードのみがパイプライン作成時に考慮されます。 つまり、エントリーポイントに関係しないコードはコンパイル前に実質的に除去されます。 -
注意: 各シェーダーステージは個別にコンパイルされるとみなされるため、 モジュールの異なる部分を含む場合があります。
-
-
シェーダー実行開始
-
これは描画またはディスパッチコマンドがGPUに発行され、 パイプラインの実行が開始され、 シェーダーステージのエントリーポイント関数が呼び出されるときに発生します。
-
-
シェーダー実行終了
これらのイベントは次の理由で順序付けられます:
-
データ依存性:シェーダー実行はパイプラインを必要とし、パイプラインはシェーダーモジュールを必要とします。
-
因果関係:シェーダーは実行開始しなければ終了できません。
2.2. エラー
WebGPUの実装がシェーダーの処理に失敗する理由は2つあります:
-
プログラムエラー は、シェーダーがWGSLやWebGPUの仕様要件を満たしていない場合に発生します。
-
未分類エラーは、 WGSLやWebGPUの要件をすべて満たしている場合でも発生することがあります。 その原因例:
-
シェーダーが複雑すぎて実装の能力を超えている(ただし制限で容易に捕捉できない方法で)。 シェーダーを単純化することで回避できる場合があります。
-
WebGPU実装の不具合。
-
処理エラーはシェーダーのライフサイクルの3段階で発生する場合があります:
-
シェーダー作成エラー は、シェーダーモジュール作成時に現実的に検出可能なエラーです。 検出にはWGSLモジュールのソーステキストと
createShaderModule
APIメソッドで得られる他の情報のみが利用されます。 この仕様でプログラムが必須とされていることに違反した場合、通常はシェーダー作成エラーとなります。 -
パイプライン作成エラー は、パイプライン作成時に検出可能なエラーです。 検出にはWGSLモジュールのソーステキストと、該当するパイプライン作成APIメソッドで得られる他の情報が利用されます。 これらのエラーは、シェーダーの エントリーポイント用にコンパイルされる
GPUProgrammableStage
のコードに対してのみ発生します。 -
動的エラーはシェーダーの実行中に発生するエラーです。 これらのエラーは検出される場合もされない場合もあります。
注意: 例えば、データレースは検出できない場合があります。
各要件willは最も早く検出可能なタイミングでチェックされます。 つまり:
-
シェーダー作成時に検出できる要件違反はシェーダー作成エラーとなります。
-
パイプライン作成時にのみ検出できる要件違反はパイプライン作成エラーとなります。
文脈からはっきりしない場合、この仕様書は 特定の要件の違反が シェーダー作成エラー・パイプライン作成エラー・動的エラーのどれにつながるかを明示します。
エラーの結果は以下の通りです:
-
シェーダー作成エラーやパイプライン作成エラーのあるWGSLモジュールは、 パイプラインに組み込まれず、実行されません。
-
動的エラーが発生した場合:
2.3. 診断
実装はシェーダーモジュール作成またはパイプライン作成の間に診断を生成できます。 診断とは、実装がアプリケーション制作者のために出力するメッセージです。
診断は、特定の条件が満たされたときに作成またはトリガーされます。 この条件をトリガールールと呼びます。 条件が満たされたソーステキスト中の場所(点または範囲)は トリガー位置と呼ばれます。
診断は次のプロパティを持ちます:
診断の重大度は以下のいずれかであり、高い順に並んでいます:
- error(エラー)
-
診断はエラーです。 これはシェーダー作成エラーまたはパイプライン作成エラーに相当します。
- warning(警告)
-
診断は開発者の注意を促す異常を記述しますが、エラーではありません。
- info(情報)
-
診断は開発者の注意を促す注目すべき状態を記述しますが、エラーでも警告でもありません。
- off(無効)
-
診断は無効化されています。アプリケーションには通知されません。
トリガールールの名前は、以下のいずれかです:
-
2つのdiagnostic_name_tokenをピリオド
'.'
(U+002E)で区切ったもの
2.3.1. 診断処理
トリガーされた診断はwill次のように処理されます:
-
各診断Dについて、Dのトリガー位置を含み、かつ同じトリガールールを持つ最小の診断フィルターを探す。
-
そのようなフィルターが存在すれば、Dに適用し、Dの重大度を更新する。
-
存在しなければDは変更されません。
-
-
重大度offの診断を破棄します。
-
残った診断のうち、少なくとも1つDIが重大度infoなら:
-
同じトリガールールを持つ他のinfo診断は破棄されることがあり、元の診断DIのみが残ります。
-
-
残った診断のうち、少なくとも1つDWが重大度warningなら:
-
残った診断にerror重大度がある場合:
-
他の診断(error重大度を含む)も破棄されることがあります。
-
プログラムエラーが生成されます。
-
エラーがシェーダーモジュール作成時にトリガーされた場合はシェーダー作成エラーです。
-
エラーがパイプライン作成時にトリガーされた場合はパイプライン作成エラーです。
-
-
-
シェーダーモジュール作成時処理の場合、残った診断はWebGPU
messages
メンバーに格納されます(GPUCompilationInfo
オブジェクト)。 -
パイプライン作成時処理の場合、error診断はWebGPUの検証失敗につながります(
GPUProgrammableStage
の検証時)。
注意: これらの規則により、実装はエラーを検出した時点でWGSLモジュールの処理を打ち切ることができます。 また、ある種の警告分析は最初の警告で打ち切ることができ、ある種の情報診断分析も最初の発生で打ち切ることができます。 WGSLはさまざまな分析手順の順序や、単一分析内の順序を規定しません。 したがって、同じWGSLモジュールでも、異なる実装は同じ重大度の診断のインスタンスを異なる数だけ報告する場合があります。
2.3.2. フィルタ可能なトリガールール
ほとんどの診断は無条件でWebGPUアプリケーションに報告されます。 一部の診断は、フィルタ可能であり、トリガールールの名前付けにより部分的に制御できます。 以下の表はフィルタ可能な標準的トリガールールを示します。
フィルタ可能トリガールール | デフォルト重大度 | トリガー位置 | 説明 |
---|---|---|---|
derivative_uniformity | error | 微分を計算する組み込み関数の呼び出し位置。 つまり、以下への呼び出しの位置: |
組み込み関数呼び出しで微分を計算するが、均一性解析がその呼び出しが均一な制御フロー内で発生することを証明できない場合。
§ 15.2 均一性も参照。 |
subgroup_uniformity | error | サブグループやクアッド組み込み関数の呼び出し位置。 |
サブグループまたはクアッド組み込み関数呼び出しだが、均一性解析がその呼び出しが均一な制御フロー内で発生することを証明できない場合。
また、均一性解析が以下のパラメータ値の均一性を証明できない場合:
§ 15.2 均一性も参照。 |
1つのdiagnostic name-tokenで構成される認識されないトリガールールを使うと、ユーザーエージェントは警告をトリガーするべきです。
実装はここで定義されていないトリガールールもサポートしてよいですが、 diagnostic_rule_nameの複数トークン形式で記述されている必要があります。 未認識のトリガールールを複数トークン形式で記述した場合、それ自体が診断をトリガーすることがあります。
将来の仕様書バージョンでは、特定のルールを削除したり、デフォルト重大度を弱めたり(現在のデフォルトより軽くする)しても後方互換性を満たしたとみなされます。
例えば、WGSLの将来バージョンでderivative_uniformityのデフォルト重大度をerror
からwarning
またはinfo
に変更することがあります。
このような仕様変更後も、以前は有効だったプログラムは引き続き有効です。
2.3.3. 診断フィルター
フィルタ可能なトリガールールを持つ診断がトリガーされると、WGSLはその診断を破棄したり重大度を変更する仕組みを提供します。
診断フィルター DFは3つのパラメータを持ちます:
診断フィルターDF(AR,NS,TR)を診断Dに適用すると、以下の効果があります:
範囲診断フィルターは、
診断フィルターのうち、影響範囲が指定されたソーステキストの範囲であるものです。
範囲診断フィルターは、影響範囲の先頭に@diagnostic
属性として指定します。
@diagnostic
属性は他の場所には現れてはなりません。
配置 | 影響範囲 |
---|---|
複合文の先頭 | その複合文 |
関数宣言の先頭 | 関数宣言 |
if文の先頭 | if文:if_clauseおよび関連するelse_if_clause、else_clause、制御条件式すべてを含む |
switch文の先頭 | switch文:セレクター式とswitch_body |
switch_bodyの先頭 | switch_body |
loop文の先頭 | loop文 |
while文の先頭 | while文:条件式とループ本体両方 |
for文の先頭 | for文:for_headerとループ本体 |
ループ本体の開始波括弧('{ ')直前
| ループ本体 |
continuing_compound_statementの先頭 | continuing_compound_statement |
注意: 以下も複合文です: 関数本体、case句、default-alone句、 whileやforループの本体、 if_clause、else_if_clause、else_clauseの本体。
var < private> d : f32; fn helper () -> vec4< f32> { // "if"の本体でderivative_uniformity診断を無効化。 if ( d < 0.5 ) @diagnostic ( off , derivative_uniformity ) { return textureSample ( t , s , vec2( 0 , 0 )); } return vec4( 0.0 ); }
グローバル診断フィルターを使うと、WGSLモジュール全体に診断フィルターを適用できます。
diagnostic ( off , derivative_uniformity ); var < private> d : f32; fn helper () -> vec4< f32> { if ( d < 0.5 ) { // グローバル診断フィルターでderivative_uniformity診断が無効化されています。 return textureSample ( t , s , vec2( 0 , 0 )); } else { // derivative_uniformity診断は'warning'重大度に設定されています。 @diagnostic ( warning , derivative_uniformity ) { return textureSample ( t , s , vec2( 0 , 0 )); } } return vec4( 0.0 ); }
2つの診断フィルター DF(AR1,NS1,TR1)とDF(AR2,NS2,TR2)が 競合するのは以下の場合です:
-
(AR1 = AR2)、かつ
-
(TR1 = TR2)、かつ
-
(NS1 ≠ NS2)。
注意: 競合しない場合、複数のグローバル診断フィルターの利用が可能です。
WGSLの診断フィルターは、その影響範囲が完全に入れ子となるように設計されています。 DF1の影響範囲がDF2の影響範囲と重なる場合、DF1の影響範囲はDF2の影響範囲に完全に含まれるか、その逆です。
ソース位置LとトリガールールTRに対する最も近い囲み診断フィルター(存在する場合)は、DF(AR,NS,TR)で次を満たすものです:
-
Lが影響範囲ARに含まれること
-
他にDF'(AR',NS',TR)がありLがAR'に含まれる場合、ARはAR'に含まれること
影響範囲が入れ子になるため、最も近い囲み診断:
-
唯一の範囲診断フィルターとなる、
-
または重複した(競合しない)グローバル診断フィルターのひとつ、
-
または存在しない。
2.4. 制限
WGSLの実装は以下の制限を満たすシェーダーをサポートするwill必要があります。 WGSLの実装は、指定された制限を超えるシェーダーもサポートしてよいです。
注意: WGSL実装は、指定された制限を超えるシェーダーをサポートしない場合エラーを出すべきです。
制限 | 最低サポート値 |
---|---|
構造体型のメンバー最大数 | 1023 |
複合型の入れ子深度の最大値 | 15 |
関数内の波括弧で囲まれた文の最大入れ子深度 | 127 |
関数のパラメータ最大数 | 255 |
switch文のcaseセレクター値最大数。 各case文のcase値数およびdefault句の合計。 | 1023 |
privateアドレス空間で1つのシェーダーによって静的にアクセスされる変数すべての合計バイトサイズ最大値 | 8192 |
functionアドレス空間で1つの関数に宣言された変数すべての合計バイトサイズ最大値 | 8192 |
workgroupアドレス空間で1つのシェーダーによって静的にアクセスされる変数すべての合計バイトサイズ最大値
この制限のため、固定フットプリント配列は、オーバーライド値で置き換える際に作成時固定フットプリント配列として扱われます。 これはWebGPUのmaxComputeWorkgroupStorageSize 制限をWGSL独自の制限へマッピングします。 | 16384 |
value constructor式のarray型における要素最大数 | 2047 |
3. テキスト構造
text/wgsl
メディアタイプは、WGSLモジュールとしてコンテンツを識別するために使われます。
付録A: text/wgslメディアタイプを参照してください。
WGSLモジュールはUTF-8でエンコードされたUnicodeテキストで、バイトオーダーマーク(BOM)はありません。
WGSLモジュールのテキストは、Unicodeのコードポイントの並びであり、連続した空でない集合にグループ化されて以下を形成します:
プログラムテキストはヌルコードポイント(U+0000
)を含んではなりません。
3.1. 構文解析
WGSLモジュールを構文解析する手順:
コメントを削除:
最初のコメントをスペースコードポイント(
U+0020
)に置換。コメントが残っている限り繰り返す。
テンプレートリストを、 § 3.9 テンプレートリストのアルゴリズムで検出。
全文を、translation_unit構文規則にマッチさせて解析する。 解析にはLALR(1)パーサ(一つ先読み)[DeRemer1969]を使い、以下のカスタマイズがあります:
字句解析(トークン化)は構文解析と交互に行われ、文脈依存です。 パーサが次のトークンを要求したとき:
最初の空白コードポイントの並びを消費して無視する。
次のコードポイントがテンプレートリストの開始なら、それを消費して_template_args_startを返す。
次のコードポイントがテンプレートリストの終了なら、それを消費して_template_args_endを返す。
それ以外の場合:
トークン候補は、残りの未消費コードポイントの非空接頭辞から形成されるWGSLのトークンです。
返すトークンは、現在のパーサ状態に対して有効な先読みトークンでもある、最長のトークン候補です。[VanWyk2007]
次の場合はシェーダー作成エラーとなります:
-
ソーステキスト全体が有限個の有効なトークン列に変換できない場合
-
translation_unit構文規則がトークン列全体にマッチしない場合
3.2. 空白と改行
空白は、 UnicodeのPattern_White_Spaceプロパティのいずれかのコードポイント1つ以上の組み合わせです。 以下はPattern_White_Spaceに含まれるコードポイントの集合です:
-
スペース(
U+0020
) -
水平タブ(
U+0009
) -
改行(
U+000A
) -
垂直タブ(
U+000B
) -
フォームフィード(
U+000C
) -
復帰(
U+000D
) -
次行(
U+0085
) -
左から右マーク(
U+200E
) -
右から左マーク(
U+200F
) -
行区切り(
U+2028
) -
段落区切り(
U+2029
)
改行は、 空白コードポイントの連続で、行の終端を示します。 これはUAX14 Section 6.1 Non-tailorable Line Breaking Rules、LB4、LB5で規定される「必須改行」です。 すなわち、改行となるのは以下のいずれかです:
-
改行(
U+000A
) -
垂直タブ(
U+000B
) -
フォームフィード(
U+000C
) -
復帰(
U+000D
)で、その後に改行(U+000A
)が続かない場合 -
復帰(
U+000D
)の直後に改行(U+000A
)が続く場合 -
次行(
U+0085
) -
行区切り(
U+2028
) -
段落区切り(
U+2029
)
注意: ソーステキストの行番号で報告する診断は、改行を使って行数を数えるべきです。
3.3. コメント
コメントは、 WGSLプログラムの妥当性や意味に影響しないテキスト範囲ですが、コメントはトークンを分離することができます。 シェーダー作者はコメントを使ってプログラムを記録できます。
行末コメントはコメントの一種で、
次の2つのコードポイント//
(U+002F
、U+002F
)と、その後に続くコードポイントからなります。
ただし、以下のいずれかまで(含まず):
-
次の改行
-
プログラムの終端
ブロックコメントはコメントの一種で、以下からなります:
-
2つのコードポイント
/*
(U+002F
、U+002A
) -
その後、以下のいずれかの並び:
-
*/
(U+002A
、U+002F
)または/*
(U+002F
、U+002A
)を含まないテキスト
-
最後に2つのコードポイント
*/
(U+002A
、U+002F
)
注意: ブロックコメントは入れ子にできます。 ブロックコメントは開始と終了のテキスト一致が必要で、任意の入れ子を許すため、正規表現で認識することはできません。 これは正規言語のポンピング補題の帰結です。
const f = 1.5 ; // これは行末コメントです。 const g = 2.5 ; /* これは複数行にまたがるブロックコメントです /* ブロックコメントは入れ子にできます。 */ ただしすべてのブロックコメントは終了しなければなりません。 */
3.4. トークン
トークンは、以下のいずれかを構成する連続したコードポイントの並びです:
3.5. リテラル
リテラルは以下のいずれかです:
3.5.1. 真偽値リテラル
'true'
| 'false'
3.5.2. 数値リテラル
数値リテラルの形式はパターンマッチングで定義されます。
整数リテラルは以下の通り:
-
整数は次のいずれかで指定:
-
0
-
最初の数字が
0
でない10進数字の並び -
0x
または0X
に続く16進数字の並び
-
-
その後、オプションで
i
またはu
サフィックス
注意: 非ゼロ整数リテラルの先頭ゼロ(例:012)は禁止です。他言語の先頭ゼロ=8進数記法との混同を避けるためです。
/0[iu]?/
| /[1-9][0-9]*[iu]?/
/0[xX][0-9a-fA-F]+[iu]?/
浮動小数点リテラルは10進浮動小数点リテラルまたは16進浮動小数点リテラルです。
浮動小数点リテラルは、分数を表す仮数部と、オプションの指数部の2つの論理部分を持ちます。 リテラルの値は概ね、仮数部を基数^指数の値で乗算したものです。 仮数部の数字は、ゼロでない場合、または左と右にゼロでない数字が両方ある場合、有効桁とみなされます。 有効桁は左から右へ数えます。N番目の有効桁は左にN-1個の有効桁があります。
10進浮動小数点リテラルは以下の通り:
-
仮数部は数字の並びで、小数点(
.
)がどこかに現れてもよいです。 仮数部は10進表記の分数を表します。 -
その後、オプションで指数部(以下の並び):
-
e
またはE
-
符号(
+
または-
)を先頭に付けてもよい10進数の指数 -
その後、オプションで
f
またはh
サフィックス
-
-
小数点、指数、
f
またはh
サフィックスのうち、少なくとも1つは必須です。 どれもない場合、そのトークンは整数リテラルです。
/0[fh]/
| /[1-9][0-9]*[fh]/
| /[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?[fh]?/
| /[0-9]+\.[0-9]*([eE][+-]?[0-9]+)?[fh]?/
| /[0-9]+[eE][+-]?[0-9]+[fh]?/
const a = 0.e+4f ; const b = 01. ; const c = .01 ; const d = 12.34 ; const f = .0f ; const g = 0h ; const h = 1e-3 ;
-
significandからeffective_significandを計算:
-
significandが20桁以下の有効桁なら、effective_significandはsignificandです。
-
それ以外:
-
20桁目より右の各桁を0に置換したものがtruncated_significand。
-
20桁目を1増やし、必要に応じて左に桁上げし、右の桁を0にしたものがtruncated_significand_next。
-
effective_significandはtruncated_significandまたはtruncated_significand_next(実装依存)
-
-
-
リテラルの値はeffective_significandを10進分数として解釈し、指数をべき乗した10で乗算したものです。 指数がなければ0とみなします。
注意: 小数部は20桁以降切り捨て、約log(10)/log(2)×20 ≈ 66.4ビットの有効精度が保持されます。
16進浮動小数点リテラルは以下の通り:
-
0x
または0X
プリフィックス -
その後、16進数字の並びで、どこかに16進小数点(
.
)があってもよい 仮数部は16進表記の分数を表します。 -
その後、オプションで指数部(以下の並び):
-
p
またはP
-
符号(
+
または-
)を先頭に付けてもよい10進数の指数 -
その後、オプションで
f
またはh
サフィックス
-
/0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+([pP][+-]?[0-9]+[fh]?)?/
| /0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*([pP][+-]?[0-9]+[fh]?)?/
| /0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+[fh]?/
const a = 0xa.fp+2 ; const b = 0x1P+4f ; const c = 0X.3 ; const d = 0x3p+2h ; const e = 0X1.fp-4 ; const f = 0x3.2p+2h ;
-
significandからeffective_significandを計算:
-
significandが16桁以下の有効桁なら、effective_significandはsignificandです。
-
それ以外:
-
16桁目より右の各桁を0に置換したものがtruncated_significand。
-
16桁目を1増やし、必要に応じて左に桁上げし、右の桁を0にしたものがtruncated_significand_next。
-
effective_significandはtruncated_significandまたはtruncated_significand_next(実装依存)
-
-
-
リテラルの値はeffective_significandを16進分数として解釈し、指数をべき乗した2で乗算したものです。 指数がなければ0とみなします。
注意: 16進仮数部は16桁以降切り捨て、約4×16=64ビットの有効精度が保持されます。
数値リテラルにサフィックスがある場合、そのリテラルは特定の具象スカラー型の値を表します。 サフィックスがない場合、リテラルは下記の抽象数値型のいずれかを表します。 いずれの場合も、リテラルの値は型変換後の数学的値であり、 § 15.7.6 浮動小数点変換の規則に従います。
数値リテラル | サフィックス | 型 | 例 |
---|---|---|---|
整数リテラル | i
| i32 | 42i |
整数リテラル | u
| u32 | 42u |
整数リテラル | AbstractInt | 124 | |
浮動小数点リテラル | f
| f32 | 42f 1e5f 1.2f 0x1.0p10f |
浮動小数点リテラル | h
| f16 | 42h 1e5h 1.2h 0x1.0p10h |
浮動小数点リテラル | AbstractFloat | 1e5 1.2 0x1.0p10 |
次の場合はシェーダー作成エラーとなります:
-
整数リテラルに
i
やu
サフィックスが付き、対象型で表せない場合 -
16進浮動小数点リテラルに
f
やh
サフィックスが付き、型であふれるか、正確に表現できない場合 -
10進浮動小数点リテラルに
f
やh
サフィックスが付き、型であふれる場合
注意: 16進浮動小数点値0x1.00000001p0は正確に表すには33有効ビットが必要ですが、f32は明示的有効ビットが23しかありません。
注意:
16進floatリテラルを型指定したい場合、f
サフィックスを使うなら2進指数も必要です。例:0x1p0f
。0x1f
は16進整数リテラルです。
3.6. キーワード
キーワードは、あらかじめ定義された言語概念を指すトークンです。 WGSLキーワード一覧は§ 16.1 キーワード一覧を参照してください。
3.7. 識別子
識別子は、名前として使用されるトークンの一種です。 詳細は§ 5 宣言とスコープを参照してください。
WGSLは用途ごとに2つの文法非終端記号を使い分けています:
-
identは宣言されたオブジェクトの名前に使われます。
-
member_identは構造体型のメンバーの名前に使われます。
識別子の形式は、Unicode Standard Annex #31 (Unicode Version 14.0.0)に基づき、 以下の補足があります。
識別子はUAX31文法で次のプロファイルを使います:
<Identifier> := <Start> <Continue>* (<Medial> <Continue>+)* <Start> := XID_Start + U+005F <Continue> := <Start> + XID_Continue <Medial> :=
つまり、非ASCIIコードポイントを含む識別子も有効です:Δέλτα
、réflexion
、Кызыл
、𐰓𐰏𐰇
、朝焼け
、سلام
、検定
、שָׁלוֹם
、गुलाबी
、փիրուզ
など。
ただし以下は例外です:
/([_\p{XID_Start}][\p{XID_Continue}]+)|([\p{XID_Start}])/u
Unicode Character Database for Unicode Version 14.0.0には XID_Startおよび XID_Continueの全有効コードポイントが非規範的に列挙されています。
注意: 一部の組み込み関数の戻り値型は、名前がWGSLソースで使用できない構造体型です。
それらの構造体型は、名前がアンダースコア2文字で始まるものとして事前宣言されたものとみなされます。
結果値は新しく宣言したlet
やvar
に型推論で保存するか、そのメンバーを直接名前指定で抽出できます。
使用例はfrexp
やmodf
の説明を参照してください。
3.7.1. 識別子の比較
2つのWGSL識別子は、同じコードポイントの並びである場合に限り同一です。
注意: この仕様では比較時のUnicode正規化を認めていません。 見た目や意味が同じでも、異なるUnicode文字列の並びであれば一致しません。 著者は値のエンコード順序を一貫して使うか、問題を起こしそうな文字を避けることが推奨されます。 詳細は[CHARMOD-NORM]参照。
注意: ユーザーエージェントは、識別子のすべての出現箇所を、その同形異義語(ホモグリフ)で置換した場合WGSLモジュールの意味が変わるとき、 開発者向け警告を出すべきです。 (ホモグリフとは、読者に同じに見える可能性のあるコードポイント列のこと。 マッピング検出例は前段落の変換・マッピング・照合アルゴリズムなど。 反復的に部分列をホモグリフで置換することで識別子を変換できる場合、2つの列は同形異義語とみなされます。)
3.8. 文脈依存名
文脈依存名は、特定の文法的文脈でのみ概念の名前として使われるトークンです。 トークンの綴りは識別子と同じ場合もありますが、そのトークンは宣言されたオブジェクトに解決されません。 このセクションでは、文脈依存名として使われるトークンを列挙します。 トークンはキーワードや予約語であってはなりません。
3.8.1. 属性名
§ 12 属性を参照してください。
属性名:
3.8.2. 組み込み値名
組み込み値名トークンは、組み込み値の名前に使われるトークンです。
組み込み値名:
3.8.3. 診断ルール名
診断名トークンは、診断トリガールールの名前に使われるトークンです。
§ 2.3 診断参照。
あらかじめ定義された診断ルール名:
3.8.4. 診断重大度制御名
有効な診断フィルター重大度制御名は§ 2.3 診断に記載されていますが、形式は識別子と同じです:
診断フィルター重大度制御名:
3.8.5. 拡張名
有効な拡張有効化名は§ 4.1.1 拡張の有効化に記載されていますが、一般的には識別子と同じ形式です:
拡張有効化名:
有効な言語拡張名は§ 4.1.2 言語拡張に記載されていますが、一般的には識別子と同じ形式です:
言語拡張名:
3.8.6. 補間型名
補間型名トークンは、補間型の名前に使われるトークンです。 interpolate_type_name用。
補間型名:
3.8.7. 補間サンプリング名
補間サンプリング名トークンは、補間サンプリングの名前に使われるトークンです。
補間サンプリング名:
3.8.8. スウィズル名
/[rgba]/
| /[rgba][rgba]/
| /[rgba][rgba][rgba]/
| /[rgba][rgba][rgba][rgba]/
| /[xyzw]/
| /[xyzw][xyzw]/
| /[xyzw][xyzw][xyzw]/
| /[xyzw][xyzw][xyzw][xyzw]/
3.9. テンプレートリスト
テンプレートパラメータ化は、一般的な概念を修飾するためのパラメータを指定する方法です。 テンプレートパラメータ化を書くには、一般概念の後にテンプレートリストを書きます。
テンプレートパラメータの形式は、下記のテンプレートリスト発見アルゴリズムで暗黙的に定義されます。 一般的には名前、式、または型です。
注:
例えば、vec3<f32>
はテンプレートパラメータ化で、vec3
が修飾される一般概念、
<f32>
がテンプレートリスト(パラメータはf32型)。
vec3<f32>
は具体的なベクトル型を表します。
注:
例えば、var<storage,read_write>
は一般的なvar
概念を、テンプレートパラメータstorage
とread_write
で修飾します。
array<vec4<f32>>
は2つのテンプレートパラメータ化を持ちます:
-
vec4<f32>
は一般的なvec4
概念をテンプレートパラメータf32
で修飾します。 -
array<vec4<f32>>
は一般的なarray
概念をテンプレートパラメータvec4<f32>
で修飾します。
テンプレートリストを区切る'<'
(U+003C)と'>'
(U+003E)コードポイントは、以下の場合にも使われます:
-
relational_expressionの比較演算子
-
shift_expressionのシフト演算子
-
compound_assignment_operatorによるシフト+代入演算
構文上の曖昧さはテンプレートリストを優先して解決されます:
-
後段のトークン化時、テンプレートリストの先頭
'<'
(U+003C)は_template_args_startトークン、終端'>'
(U+003E)は_template_args_endトークンに変換されます。
テンプレートリスト発見アルゴリズムは下記。 以下の前提・性質を使います:
-
テンプレートパラメータは式であり、
'<'
(U+003C)や'='
(U+003D)で始まらない。 -
式は
';'
(U+003B)、'{'
(U+007B)、':'
(U+003A)を含まない。 -
式には代入文は含まれない。
-
'='
(U+003D)が現れるのは比較演算('<='
,'>='
,'=='
,'!='
)の場合のみ。他は代入の一部。 -
テンプレートリスト区切りは括弧'(...)'や配列インデックス'[...]'による入れ子式に従い、開始・終了は同じ入れ子レベルで現れる。
アルゴリズム: テンプレートリスト発見入力: プログラムのソーステキスト。
レコード型:
UnclosedCandidateは以下を含むレコード型:
position:ソーステキスト上の位置
depth:positionでの式入れ子深度(整数)
TemplateListは以下を含むレコード型:
start_position:このテンプレートリストを開始する
'<'
(U+003C)コードポイントのソース位置end_position:このテンプレートリストを終了する
'>'
(U+003E)コードポイントのソース位置出力: DiscoveredTemplateLists(TemplateListレコードのリスト)
手順:
DiscoveredTemplateListsを空リストで初期化。
Pending変数をUnclosedCandidateレコードの空スタックで初期化。
CurrentPosition整数変数を0で初期化。 現在調べているコードポイントの位置を、ソーステキスト開始からのコードポイント数として表現。
この変数はアルゴリズム実行中に前方へ進み、テキスト終端に達したら即座にアルゴリズムを終了し、DiscoveredTemplateListsを返す。
NestingDepth整数変数を0で初期化。
以下を繰り返す:
ident_pattern_tokenがCurrentPositionのテキストにマッチした場合:
CurrentPositionをident_pattern_tokenの直後まで進める。
空白・コメントがあればCurrentPositionをさらに進める。
'<'
(U+003C)がCurrentPositionに現れたら:
注: このコードポイントはテンプレートリスト開始の候補。 後で終端
'>'
(U+003E)と対応付けできるよう状態を保存。UnclosedCandidate(position=CurrentPosition,depth=NestingDepth) をPendingスタックに積む。
CurrentPositionを次のコードポイントに進める。
'<'
(U+003C)がCurrentPositionに現れたら:
注: 前提1より、いかなるテンプレートパラメータも
'<'
(U+003C)で始まらないので、直前のコードポイントはテンプレートリスト開始ではない。 よって現在・前のコードポイントは'<<'
演算子。Pendingスタックからトップをpop。
CurrentPositionを進めて次のループへ。
'='
(U+003D)がCurrentPositionに現れたら:
注: 前提1より、いかなるテンプレートパラメータも
'='
(U+003C)で始まらないので、直前のコードポイントはテンプレートリスト開始ではない。 よって現在・前のコードポイントは'<='
比較演算子。'='
(U+003D)を後の代入と誤認しないようスキップ。Pendingスタックからトップをpop。
CurrentPositionを進めて次のループへ。
次のループへ。
'>'
(U+003E)がCurrentPositionに現れたら:
注: このコードポイントはテンプレートリスト終端の候補。
Pendingが空でなければトップTを取得し、T.depthがNestingDepthと等しければ:
注: このコードポイントはT記録のテンプレートリストの終端。
TemplateList(start_position=T.position, end_position=CurrentPosition)をDiscoveredTemplateListsに追加。
PendingスタックからTをpop。
CurrentPositionを進めて次のループへ。
それ以外の場合、このコードポイントはテンプレートリスト終端ではない:
CurrentPositionを進める。
'='
(U+003D)がCurrentPositionに現れたら:
注: 現在・前のコードポイントは
'>='
比較演算子。'='
(U+003D)を後の代入と誤認しないようスキップ。CurrentPositionを進める。
次のループへ。
'('
(U+0028)または'['
(U+005B)がCurrentPositionに現れたら:
注: 入れ子式の開始。
NestingDepthに1加算。
CurrentPositionを進めて次のループへ。
')'
(U+0029)または']'
(U+005D)がCurrentPositionに現れたら:
注: 入れ子式の終了。
Pendingスタックから、空になるかトップのdepthがNestingDepthより小さいものになるまでpop。
NestingDepthを0またはNestingDepth−1のいずれか大きい方に設定。
CurrentPositionを進めて次のループへ。
'!'
(U+0021)がCurrentPositionに現れたら:
CurrentPositionを進める。
'='
(U+003D)がCurrentPositionに現れたら:
注: 現在・前のコードポイントは
'!='
比較演算子。'='
(U+003D)を後の代入と誤認しないようスキップ。CurrentPositionを進める。
次のループへ。
'='
(U+003D)がCurrentPositionに現れたら:
CurrentPositionを進める。
'='
(U+003D)がCurrentPositionに現れたら:
注: 現在・前のコードポイントは
'=='
比較演算子。'='
(U+003D)を後の代入と誤認しないようスキップ。CurrentPositionを進めて次のループへ。
注: これは代入の一部で、式中やテンプレートリスト中では現れない。 未閉候補をクリア。
NestingDepthを0に設定。
Pendingスタックを空に。
CurrentPositionを進めて次のループへ。
';'
(U+003B)、'{'
(U+007B)、':'
(U+003A)がCurrentPositionに現れたら:
注: これらは式中やテンプレートリスト中では現れない。未閉候補をクリア。
NestingDepthを0に設定。
Pendingスタックを空に。
CurrentPositionを進めて次のループへ。
'&&'
または'||'
がCurrentPositionのテキストにマッチした場合:
注: これらは比較より優先度の低い演算子。現在の式レベルの未閉候補を破棄。
注: この規則により、
a<b || c>d
のような断片にテンプレートリストは見つからず、2つの比較の短絡ORと認識される。Pendingスタックから、空になるかトップのdepthがNestingDepthより小さいものになるまでpop。
2コードポイント分CurrentPositionを進めて次のループへ。
現在のコードポイント分CurrentPositionを進める。
-
UnclosedCandidateを以下のフィールド追加で修正:
-
parameters:テンプレートパラメータのソース範囲のリスト
-
parameter_start_position:ソース位置
-
-
TemplateListを以下のフィールド追加で修正:
-
parameters:テンプレートパラメータのソース範囲のリスト
-
-
UnclosedCandidateを新たにPendingスタックに積むとき:
-
parametersフィールドは空リストにする
-
parameter_start_positionはCurrentPositionの1コードポイント後に設定
-
-
TemplateList(TL)をDiscoveredTemplateListsに追加するとき:
-
TをPendingスタックのトップに
-
開始位置T.parameter_start_positionから終了位置CurrentPosition−1までの範囲をT.parametersに追加
-
元のアルゴリズム通りにTL準備
-
TL.parametersにT.parametersを設定
-
-
ループ終端直前(現在コードポイント進める前)に次のチェック追加:
-
'
,
'(U+002C)がCurrentPositionに現れ、かつPendingが空でなければ:-
TをPendingスタックのトップに
-
開始位置T.parameter_start_positionから終了位置CurrentPosition−1までの範囲をT.parametersに追加
-
T.parameter_start_positionをCurrentPosition+1に設定
-
-
注:
アルゴリズムはリテラルを明示的にスキップします。なぜなら数値リテラルの末尾に文字が来る場合があり、例えば1.0f
の末尾f
をident_pattern_tokenの開始と誤認しないようにするためです。
注:
A ( B < C, D > ( E ) )
のような語では、< C, D >
がテンプレートリストです。
注:
アルゴリズムは式の入れ子を尊重します。特定のテンプレートリストの開始と終了が異なる入れ子レベルで現れることはありません。
例えばarray<i32,select(2,3,a>b)>
では、テンプレートリストは3つのパラメータを持ち、最後はselect(2,3,a>b)
です。
a>b
の'>'
はテンプレートリスト終端にはならず、select
関数呼び出し式の括弧内に囲まれているからです。
注: テンプレートリストの両端は同じインデックス式内で現れる必要があります。例えばa[b<d]>()
は有効なテンプレートリストを含みません。
注:
A<B<<C>
では、B<<C
はB
+左シフト演算子'<<'
+C
です。
テンプレート発見アルゴリズムはB
→'<
'(U+003C)と見て、次も'<
'(U+003C)なのでテンプレート引数開始とせず、
B
直後の'<'
はテンプレートリスト開始ではありません。
最初の'<'
と最後の'>'
のみがテンプレートリスト区切りで、パラメータはB<<C
です。
注:
A<B<=C>
も同様に、B<=C
はB
+小なりイコール演算子'<='
+C
です。
テンプレート発見アルゴリズムはB
→'<'
(U+003C)→'='
(U+003D)と見て、'='
はテンプレート引数開始にならないので、B
直後の'<'
はテンプレートリスト開始ではありません。
最初の'<'
と最後の'>'
のみがテンプレートリスト区切りで、パラメータはB<=C
です。
注:
A<(B>=C)>
では、最初の'<'
(U+003C)から最後の'>'
(U+003E)までがテンプレートリストで、引数はB>=C
です。
B
後の'>'
(U+003C)コードポイント(誤記?)の後、'='
(U+003D)は代入と誤認しないよう特別扱いします。
注:
A<(B!=C)>
では、最初の'<'
(U+003C)から最後の'>'
(U+003E)までがテンプレートリストで、引数はB!=C
です。
B
後の'!'
(U+0021)コードポイントの後、'='
(U+003D)は代入と誤認しないよう特別扱いします。
注:
A<(B==C)>
では、最初の'<'
(U+003C)から最後の'>'
(U+003E)までがテンプレートリストで、引数はB==C
です。
B
後の最初の'='
(U+003D)コードポイント、次の'='
(U+003D)コードポイントもどちらも代入と誤認しないよう特別扱いします。
テンプレートリスト発見完了後、 構文解析は各テンプレートリストをtemplate_list構文規則にマッチさせます。
_template_args_start template_arg_comma_list _template_args_end
template_arg_expression ( ','
template_arg_expression ) * ','
?
4. ディレクティブ
ディレクティブは、WGSLプログラムの処理方法をWebGPU実装に変更させるトークン列です。
ディレクティブは省略可能です。 指定されている場合は、すべてのディレクティブが宣言やconstアサーションよりも前に現れる必要があります。
4.1. 拡張
WGSLは今後も進化することが期待されています。
拡張は、WGSL仕様の一貫性のある変更をまとめた名前付きグループであり、以下の任意の組み合わせで構成されます:
-
新しい構文による新しい概念や動作の追加、例:
-
宣言、文、属性、組み込み関数など。
-
-
現行標準や既存拡張での制限の除去。
-
許容される動作の範囲を減らすための構文。
-
プログラムの一部で利用可能な機能を制限するための構文。
-
拡張が既存仕様および他の拡張とどのように相互作用するかの説明。
仮に拡張は以下のようなことが可能です:
-
異なるビット幅の整数などの数値スカラー型の追加。
-
浮動小数点の丸めモードを制約する構文の追加。
-
シェーダーがアトミック型を使用しないことを示す構文の追加。
-
新しい種類の文を追加。
-
新しい組み込み関数を追加。
-
シェーダー呼び出しの実行方法を制約する構文の追加。
-
新しいシェーダーステージの追加。
4.1.1. 有効化拡張
有効化拡張は、以下の条件を満たす場合のみ利用可能な拡張です:
-
実装がそれをサポートしていること
-
シェーダーがenableディレクティブで明示的に要求していること
-
対応するWebGPU
GPUFeatureName
がGPUDevice
作成時に要求された必須機能のひとつであること
有効化拡張は、ハードウェア固有の機能を公開するために意図されています。
enableディレクティブは、ひとつ以上の有効化拡張を有効にするディレクティブです。 実装が全ての指定された有効化拡張をサポートしていない場合、シェーダー生成エラーとなります。
'enable'
enable_extension_list ';'
他のディレクティブ同様、enableディレクティブが存在する場合は、すべての宣言やconstアサーションよりも前に現れる必要があります。 拡張名は識別子ではありません。 宣言に解決されません。
有効な有効化拡張は次の表に示されています。
WGSL有効化拡張 | WebGPU GPUFeatureName
| 説明 |
---|---|---|
f16
| "shader-f16" | f16型がWGSLモジュールで利用可能になります。それ以外の場合、f16(直接または間接的に)を使用するとシェーダー生成エラーとなります。 |
clip_distances
| "clip-distances" | 組み込み変数clip_distancesがWGSLモジュールで利用可能になります。それ以外の場合、clip_distancesを使用するとシェーダー生成エラーとなります。 |
dual_source_blending
| "dual-source-blending" | 属性blend_srcがWGSLモジュールで利用可能になります。それ以外の場合、blend_srcを使用するとシェーダー生成エラーとなります。 |
subgroups
| "subgroups" | サブグループ組み込み変数、サブグループ組み込み関数、quad組み込み関数をWGSLモジュールで利用可能です。 それ以外の場合、いずれかを使用するとシェーダー生成エラーとなります。 |
primitive_index
| "primitive-index"
| 組み込み変数primitive_indexがWGSLモジュールで利用可能になります。それ以外の場合、primitive_indexを使用するとシェーダー生成エラーとなります。 |
// 任意精度浮動小数点型の仮想拡張を有効化 enable arbitrary_precision_float ; enable arbitrary_precision_float ; // 冗長なenableディレクティブも許容されます。 // 丸めモード制御用の仮想拡張を有効化 enable rounding_mode ; // 仮にarbitrary_precision_float拡張により以下が利用可能になる: // - 型f<E,M> // - 関数の戻り値、仮引数、let宣言の型として // - AbstractFloatからの値コンストラクター // - 除算演算子: / のオペランドとして // 仮にrounding_mode拡張で@rounding_mode属性が利用可能になる。 @rounding_mode ( round_to_even ) fn halve_it ( x : f < 8 , 7 > ) -> f < 8 , 7 > { let two = f < 8 , 7 > ( 2 ); return x / 2 ; // 偶数丸めモードで演算される。 }
4.1.2. 言語拡張
言語拡張 は、実装がサポートしている場合に自動的に利用可能となる拡張です。 プログラムが明示的に要求する必要はありません。
言語拡張 は、あらゆるWebGPU実装で合理的にサポート可能な機能を具現化しています。 この機能が普遍的に利用できない場合、それは一部のWebGPU実装がまだ対応していないためです。
注: 例えば、do-whileループは言語拡張となり得ます。
wgslLanguageFeatures
メンバーはWebGPU GPU
オブジェクトにおいて、
実装がサポートする言語拡張のセットを一覧します。
requiresディレクティブ は、プログラムが利用する言語拡張を記述するディレクティブです。 実装が提供する機能を変更するものではありません。 実装が必要とされる拡張のいずれかをサポートしていない場合、シェーダー生成エラーとなります。
WGSLモジュールはrequiresディレクティブを使って、非移植性の可能性や、 意図した移植性の最低限の基準を示すことができます。
注: WebGPU実装外のツールは、プログラムで利用されるすべての言語拡張がプログラム内のrequiresディレクティブで網羅されているか確認できます。
'requires'
language_extension_list ';'
language_extension_name ( ','
language_extension_name ) * ','
?
他のディレクティブ同様、requiresディレクティブが存在する場合は、すべての宣言やconstアサーションよりも前に現れる必要があります。 拡張名は識別子ではありません。 宣言に解決されません。
WGSL言語拡張 | 説明 |
---|---|
readonly_and_readwrite_storage_textures | read およびread_writeのアクセスモードがstorage textureで利用可能になります。 また、textureBarrier組み込み関数が追加されます。 |
packed_4x8_integer_dot_product | 4要素8ビット整数ベクトルを32ビット整数スカラーにパックして、dot4U8Packedおよび dot4I8Packed組み込み関数でドット積命令の入力として利用可能です。 さらに、pack4xI8、pack4xU8、 pack4xI8Clamp、pack4xU8Clamp、 unpack4xI8、unpack4xU8組み込み関数によるパック・アンパック命令が追加されます。 |
unrestricted_pointer_parameters |
制限のうち、ユーザー定義関数に関する以下を削除します:
ユーザー定義関数において、ポインター型パラメータは 以下のいずれかのアドレス空間でなければなりません: ユーザー定義関数のポインター型引数は、 そのroot identifierと 同じmemory view でなければなりません。
|
pointer_composite_access |
ルート式がポインターの場合に複合値分解式をサポートし、
参照(reference)を生成します。
例えば、 同様に、 |
注: 将来的には、WGSLは現時点で広くサポートされている言語拡張の機能すべてを含める言語拡張を定義する意図があります。 requiresディレクティブにおいて、これらは全ての共通機能をリストアップするための略記となります。 機能の集合は徐々に増加し、ある種の言語バージョンと考えることもできます。
4.2. グローバル診断フィルター
グローバル診断フィルターは、影響範囲がWGSLモジュール全体となる診断フィルターです。
これはディレクティブであり、すべてのモジュールスコープ宣言よりも前に現れます。
属性形式と同じスペルですが、先頭の@
(U+0040)コードポイントがなく、末尾にセミコロンが付きます。
'diagnostic'
diagnostic_control ';'
5. 宣言とスコープ
宣言は、識別子を 以下のいずれかのオブジェクト種別と関連付けます:
言い換えれば、宣言はオブジェクトの名前を導入します。
宣言がプログラムソース内で他の宣言の本文外に現れる場合、その宣言はモジュールスコープです。
関数宣言はモジュールスコープで現れます。 関数宣言は仮引数の宣言を含み、変数や値の宣言を本文内に含むこともできます。 これらの内包宣言はモジュールスコープではありません。
注: 他の宣言を内包できる唯一の宣言は関数宣言です。
一部のオブジェクトはWebGPU実装により提供され、WGSLモジュールソースの開始前に宣言されたものとして扱われます。 これらのオブジェクトは事前宣言されているといいます。 例えば、WGSLは以下を事前宣言します:
-
array
、ptr
、texture_2d
などの型ジェネレーター -
列挙値(例:read_write、 workgroup、rgba8unormなど)
スコープは、宣言された識別子が関連するオブジェクトを 示す可能性があるプログラムソースの位置集合です。 その識別子は、その宣言のスコープ内にあるといいます。
宣言の現れる位置によってスコープが決まります:
-
事前宣言されたオブジェクトおよびモジュールスコープで宣言されたオブジェクトは、プログラムソース全体でスコープ内です。
-
ユーザー定義関数の各仮引数は、 対応する関数本文全体でスコープ内です。 詳細は§ 11.1 ユーザー定義関数の宣言を参照してください。
-
それ以外の場合、スコープは宣言の終了直後から始まるテキスト範囲です。 詳細は§ 7 変数と値の宣言を参照してください。
同じWGSLソースプログラム内で、2つの宣言が同時に:
-
同じ識別子名を導入し、
-
同じスコープ終了位置を持つ
注: 事前宣言オブジェクトはWGSLソース内に宣言がありません。 したがって、ユーザーが指定した宣言がモジュールスコープや関数内にあっても、事前宣言オブジェクトと同じ名前を持つことができます。
識別子は文法的コンテキストによって以下のように使われます:
-
ident文法要素に一致するトークンは:
-
宣言で、宣言されるオブジェクトの名前として使われるか、
-
他の場所で宣言されたオブジェクトを示す名前として使われる(一般的なケース)
-
-
member_ident文法要素に一致するトークンは:
-
構造体値のメンバー、または構造体のメンバーへの参照を示す名前として使われる。 § 8.5.4 構造体アクセス式を参照。
identトークンが他の場所で宣言されたオブジェクトを示す名前として現れる場合、 それは何らかの宣言のスコープ内である必要があります。 識別子トークンが示すオブジェクトは次のように決定されます:
-
そのトークンが少なくとも1つの非モジュールスコープ宣言のスコープ内であれば、 そのトークンは最も近い宣言に関連するオブジェクトを示します。
注: 最も近い宣言は識別子トークンの前に現れます。
-
それ以外で、その名前を持つモジュールスコープ宣言があれば、トークンはその宣言されたオブジェクトを示します。
注: モジュールスコープ宣言は識別子トークンの前または後に現れることがあります。
-
それ以外で、その名前を持つ事前宣言オブジェクトがあれば、トークンはそのオブジェクトを示します。
上記アルゴリズムで識別子を宣言に対応付ける場合、その識別子がその宣言に解決するといいます。 同様に、その識別子が宣言されたオブジェクトに解決するといいます。
いずれかのモジュールスコープ宣言が再帰的である場合はシェーダー生成エラーです。 すなわち、宣言間にサイクルが存在してはなりません:
次の有向グラフを考えます:
各ノードは宣言Dに対応します。
宣言Dの定義が識別子を参照し、その識別子が宣言Tに解決する場合、宣言Dから宣言Tへの辺が存在します。
このグラフにサイクルがあってはなりません。
注: 関数本文は関数宣言の一部なので、 関数も直接・間接のいずれも再帰的であってはなりません。
注: 非モジュールスコープ識別子宣言は利用より前に現れる必要があります。
// 有効:ユーザー定義変数は組み込み関数と同じ名前を持てる。 var < private> modf : f32= 0.0 ; // 有効:foo_1はプログラム全体でスコープ内。 var < private> foo : f32= 0.0 ; // foo_1 // 有効:bar_1はプログラム全体でスコープ内。 var < private> bar : u32= 0u ; // bar_1 // 有効:my_func_1はプログラム全体でスコープ内。 // 有効:foo_2は関数終了までスコープ内。 fn my_func ( foo : f32) { // my_func_1, foo_2 // 'foo'への参照は関数引数を解決する。 // 無効:modfはモジュールスコープ変数を解決する。 let res = modf ( foo ); // 無効:foo_2のスコープは関数終了で終わる。 var foo : f32; // foo_3 // 有効:bar_2は関数終了までスコープ内。 var bar : u32; // bar_2 // 'bar'への参照はbar_2を解決する { // 有効:foo_4は複合文終了までスコープ内。 var foo : f32; // foo_4 // 有効:bar_3は複合文終了までスコープ内。 var bar : u32; // bar_3 // 'bar'への参照はbar_3を解決する // 無効:bar_4はbar_3と同じスコープ終了位置を持つ。 var bar : i32; // bar_4 // 有効:i_1はforループ終了までスコープ内 for ( var i : i32= 0 ; i < 10 ; i ++ ) { // i_1 // 無効:i_2はi_1と同じスコープ終了位置を持つ。 var i : i32= 1 ; // i_2. } } // 無効:bar_5はbar_2と同じスコープ終了位置を持つ。 var bar : u32; // bar_5 // 有効:later_def(モジュールスコープ宣言)はプログラム全体でスコープ内。 var early_use : i32= later_def ; } // 無効:bar_6はbar_1と同じスコープを持つ。 var < private> bar : u32= 1u ; // bar_6 // 無効:my_func_2はmy_func_1と同じスコープ終了位置を持つ。 fn my_func () { } // my_func_2 // 有効:my_foo_1はプログラム全体でスコープ内。 fn my_foo ( //my_foo_1 // 有効:my_foo_2は関数終了までスコープ内。 my_foo : i32// my_foo_2 ) { } var < private> later_def : i32= 1 ;
// この宣言は事前宣言された組み込み関数'min'を隠します。 // この宣言はモジュールスコープなので、ソース全体でスコープ内。 // 組み込み関数はもはや利用できません。 fn min () -> u32{ return 0 ; } const rgba8unorm= 12 ; // これは事前宣言された列挙値'rgba8unorm'をシャドウします。
6. 型
プログラムは値を計算します。
WGSLにおいて、型とは値の集合であり、各値は正確に1つの型に属します。 値の型は、その値に対して行える操作の構文と意味を決定します。
例えば、数学的な数値1はWGSLでは以下の異なる値に対応します:
-
32ビット符号付き整数値
1i
-
32ビット符号なし整数値
1u
-
32ビット浮動小数点値
1.0f
-
(f16拡張が有効な場合)16ビット浮動小数点値
1.0h
-
AbstractInt値 1
-
AbstractFloat値 1.0
WGSLはこれらを区別します。なぜなら機械表現や操作が異なるためです。
型は事前宣言されているか、WGSLソース内で宣言によって作成されます。
一部の型はテンプレートパラメータ化で表現されます。
型ジェネレーターとは、事前宣言されたオブジェクトで、テンプレートリストでパラメータ化されることで型を示します。
例えば、型atomic<u32>
は型ジェネレーターatomic
とテンプレートリスト<u32>
を組み合わせたものです。
型の概念とWGSLでその型を示す構文は区別されます。 多くの場合、この仕様における型の綴りはWGSL構文と同じです。 例えば:
-
32ビット符号なし整数値の集合は、この仕様でもWGSLモジュールでも
u32
と綴ります。 -
構造体型や構造体を含む型の場合は綴りが異なります。
一部のWGSL型は、ソースプログラムの解析や実行時挙動の決定にのみ使われます。 この仕様ではそのような型も説明しますが、WGSLソーステキストには現れません。
注: 参照型はWGSLモジュールに記述されません。§ 6.4.3 参照型とポインター型を参照してください。
6.1. 型検査
WGSL値は式の評価によって算出されます。
式とは、WGSL文法規則のうち「expression
」で終わる名前を持つものとして解析されるソーステキストの区間です。
式Eは、外側の式Eに真に内包された式である部分式を含むことがあります。
トップレベル式は、それ自身が部分式でない式です。
§ 8.18 式文法のまとめを参照。
式の評価で生成される値は次に依存します:
-
静的コンテキスト:式を囲むソーステキスト
-
動的コンテキスト:式を評価している呼び出しの状態や実行コンテキスト
特定の式を評価した結果得られる値は必ず特定のWGSL型に属します。 これはその式の静的型と呼ばれます。 WGSLの規則は、式の静的型がその式の静的コンテキストのみに依存するよう設計されています。
型アサーションは、WGSLソース式とWGSL型の対応付けです。 次の記法
e : T
は「eの静的型がTである」という型アサーションです。
注: 型アサーションはプログラムテキストについての事実の記述です。実行時検査ではありません。
文はしばしば式を用い、その式の静的型に要件を課す場合があります。例:
型検査とは、正しく構文解析されたWGSLモジュールに対して、 各式を静的型に対応付け、各文の型要件が満たされていることを検証する過程です。 型検査に失敗すると、シェーダー生成エラーの一種である型エラーとなります。
型検査は、型規則を構文的フレーズに再帰的に適用することによって行えます。 構文的フレーズは、式または文です。 型規則は、 構文的フレーズの静的コンテキストが、 その句に含まれる式の静的型をどのように決定するかを記述します。 型規則は2つの部分を持ちます:
-
結論
-
前提条件(以下を含む):
-
式の場合:
-
部分式があれば、その部分式の型アサーション。 これは直接満たしてもよいし、実現可能な自動変換によって満たしてもよい(§ 6.1.2 変換ランク参照)。 WGSLは定数式をすべて評価し、プログラムの静的型を決定します。
-
その式が文でどのように使われるか。
-
-
文の場合:
-
文の構文形式、および
-
文中のトップレベル式の型アサーション。
-
-
その他の模式パラメータに対する条件(もしあれば)。
-
オプションでその他の静的コンテキスト。
-
型規則は、前提条件や結論に型パラメータを持つことがあります。 型規則の結論や前提条件に型パラメータが含まれる場合、その規則はパラメータ化されているといいます。 含まれない場合は完全展開されているといいます。 パラメータ化された型規則に対し、各型パラメータに型を代入することで完全展開された型規則を作ることができます。 規則の型パラメータへの型の割り当てを代入と呼びます。
例えば、論理否定(!
e形式の式)の型規則は次のとおりです:
前提条件 | 結論 |
---|---|
e: T TはboolまたはvecN<bool> | ! e: T
|
これは型パラメータTを含むためパラメータ化された規則です。
Tは4つの型、bool、vec2<bool>
、vec3<bool>
、vec4<bool>
のいずれかになり得ます。
例えばTにvec3<bool>
を割り当てると、完全展開された型規則は次のようになります:
前提条件 | 結論 |
---|---|
e: vec3<bool> | ! e: vec3<bool>
|
パラメータ化された規則に対し、条件を満たす代入によって得られる完全展開規則をそれぞれオーバーロードと呼びます。 例えば、この論理否定規則は型パラメータTの割り当て方法が4通りなので、オーバーロードは4つあります。
注: つまり、パラメータ化された型規則は、異なる代入によって生成される完全展開型規則の集合のパターンを提供します。
型規則が構文的フレーズに適用されるのは、次の場合です:
-
規則の結論が構文的フレーズの有効な構文解析に一致し、
-
規則の前提条件が満たされている
パラメータ化された型規則は、ある式に対し、 代入によって完全展開型規則を生成でき、 その型規則がその式に適用できる場合に適用されます。
例えば、式1u+2u
を考えます。
これは2つのリテラル部分式(1u
と2u
、どちらもu32型)を持ちます。
トップレベル式は加算です。
§ 8.7 算術式の規則を参照すると、加算の型規則がこの式に適用されます。なぜなら:
-
1u+2u
はe1+e2という形式にパースされ、e1が1u
、e2が2u
となり、 -
e1はu32型であり、
-
e2もu32型であり、
-
型規則の型パラメータTにu32を代入できるので、式全体に適用できる完全展開規則が得られる
構文的フレーズを解析する際、以下の3つの場合があります:
-
どの型規則もその式に適用できない→型エラーとなる。
-
完全展開された型規則がちょうど1つだけその式に適用できる→その規則の結論が主張され、式の静的型が決定される。
-
複数の型規則が適用できる(複数のオーバーロードが前提条件を満たす)→§ 6.1.3 オーバーロード解決で競合解決を行う。
上記の例の通り、式1u+2u
には型規則が一つだけ適用できるため、型検査はその型規則の結論(1u+2u
がu32型)を受け入れます。
WGSLソースプログラムが型安全であるとは、次のときです:
-
全ての式について型規則を適用して静的型が決定でき、
-
すべての文の型要件が満たされている。
そうでない場合は型エラーとなり、そのソースプログラムはWGSLモジュールとして有効ではありません。
WGSLは静的型付け言語です。 なぜならWGSLモジュールの型検査は、プログラムソーステキストのみを調べて成功するか型エラーを発見するからです。
6.1.1. 型規則表
WGSLの式の型規則は、型規則表として整理されており、各行が1つの型規則を表します。
式の意味は、その式を評価したときの効果であり、主に結果値の生成です。 式に適用される型規則の説明列には、その式の意味が記載されます。 意味は通常、型規則パラメータの値や、部分式の想定値に依存します。 場合により、式の意味には結果値以外の効果(部分式の副作用など)が含まれることもあります。
fn foo ( p : ptr< function, i32> ) -> i32{ let x = * p ; * p += 1 ; return x ; } fn bar () { var a : i32; let x = foo ( & a ); // fooの呼び出しは値を返し、 // aの値を更新する }
6.1.2. 変換ランク
型アサーションe:Tが型規則の前提条件として使われる場合、次で満たされます:
-
eがすでに型Tである場合
-
eが型Sであり、型Sが型Tに自動変換可能な場合(以下定義)
この規則は、下表で定義される型の組に対するConversionRank関数で定式化されます。 ConversionRank関数は、一方の型(Src)からもう一方の型(Dest)への自動変換の優先度と実現可能性を表します。 ランクが低いほど望ましいです。
実現可能な自動変換は、型Srcから型Destへの値変換であり、 ConversionRank(Src,Dest)が有限の場合に許可されます。 これらの変換は値を保全しますが、詳細は§ 15.7 浮動小数点評価で説明される制限があります。
注: 自動変換は2つの場合のみ発生します。 1つ目は定数式をGPUで利用できる型付き数値値に変換する場合。 2つ目はメモリ参照からロードが発生し、そのメモリに格納された値が得られる場合です。
注: ランクが無限大の変換は実現不可能、つまり許可されません。
注: 変換を行わない場合、変換ランクは0です。
Src | Dest | ConversionRank(Src,Dest) | 説明 |
---|---|---|---|
T | T | 0 | 同一。変換は行わない。 |
ref<AS,T,AM> (アドレス空間 AS、 アクセスモード AMがreadまたはread_writeの場合) | T | 0 | ロード規則を適用し、メモリ参照から値をロードする。 |
AbstractFloat | f32 | 1 | § 15.7.6 浮動小数点変換参照 |
AbstractFloat | f16 | 2 | § 15.7.6 浮動小数点変換参照 |
AbstractInt | i32 | 3 | 値がi32の場合は同一変換。 そうでなければシェーダー生成エラー。 |
AbstractInt | u32 | 4 | 値がu32の場合は同一変換。 そうでなければシェーダー生成エラー。 |
AbstractInt | AbstractFloat | 5 | § 15.7.6 浮動小数点変換参照 |
AbstractInt | f32 | 6 | AbstractInt→AbstractFloat、その後AbstractFloat→f32と同じ挙動 |
AbstractInt | f16 | 7 | AbstractInt→AbstractFloat、その後AbstractFloat→f16と同じ挙動 |
vecN<S> | vecN<T> | ConversionRank(S,T) | 要素型の変換ランクを継承 |
matCxR<S> | matCxR<T> | ConversionRank(S,T) | 要素型の変換ランクを継承 |
array<S,N> | array<T,N> | ConversionRank(S,T) | 要素型の変換ランクを継承 注:固定長配列のみ抽象型要素を持てます。 |
__frexp_result_abstract | __frexp_result_f32 | 1 | |
__frexp_result_abstract | __frexp_result_f16 | 2 | |
__frexp_result_vecN_abstract | __frexp_result_vecN_f32 | 1 | |
__frexp_result_vecN_abstract | __frexp_result_vecN_f16 | 2 | |
__modf_result_abstract | __modf_result_f32 | 1 | |
__modf_result_abstract | __modf_result_f16 | 2 | |
__modf_result_vecN_abstract | __modf_result_vecN_f32 | 1 | |
__modf_result_vecN_abstract | __modf_result_vecN_f16 | 2 | |
S | T (上記に該当しない場合) | infinity | 他の型間で自動変換はありません。 |
型T
は型S
の具象化であるのは、次の場合です:
-
T
が具象型であり -
T
が参照型でない -
ConversionRank(
S
,T
)が有限である -
他の非参照型
T2
について、ConversionRank(S
,T2
) > ConversionRank(S
,T
)
型T
の値e
の具象化は、
T
からTの具象化への実現可能な変換をe
に適用した結果です。
注: f32への変換は常にf16より優先されるため、 自動変換でf16が生成されるのは、 モジュールでf16拡張が有効な場合のみです。
6.1.3. オーバーロード解決
複数の型規則が構文的フレーズに適用できる場合、 どれを適用するかを決定するための競合解決手順が用いられます。 この手順をオーバーロード解決と呼びます。 型検査によって部分式の静的型がすでに決定されていることを前提とします。
構文的フレーズPと、Pに適用できるすべての型規則を考えます。 オーバーロード解決アルゴリズムはこれらの型規則をオーバーロード候補と呼びます。 各候補について:
Pのオーバーロード解決は以下の手順で進み、最も望ましいオーバーロード候補を1つ見つけることを目的とします:
-
各候補Cについて、構文的フレーズ内の部分式ごとに変換ランクを列挙します。 候補の前提条件は満たされているため、P内のi番目の部分式について:
-
その静的型は計算済み。
-
その式の静的型から前提条件の型アサーションが要求する型への実現可能な自動変換が存在する。 C.R(i)をその変換のConversionRankとする。
-
-
部分式のいずれかが自動変換後に抽象型になるが、 その候補の他の部分式が定数式でない場合、その候補を除外する。
注: 結果として、フレーズ内のいずれかの部分式が定数式でない場合、フレーズ内のすべての部分式は具象型でなければなりません。
-
候補を順位付けする:2つのオーバーロード候補C1とC2について、C1が優先されるのは:
-
P内の各式位置iについて、C1.R(i) ≤ C2.R(i)
-
つまり、C1をPに適用するために必要な各式変換が、C2に必要な対応する式変換と同等以上に望ましい。
-
-
少なくとも1つの式位置iで、C1.R(i) < C2.R(i)
-
つまり、C1に必要な式変換のうち少なくとも1つは、C2に必要な対応する変換よりも厳密に望ましい。
-
-
-
他のすべてより優先される候補Cが1つだけあれば、オーバーロード解決は成功し、その型規則Cを返す。 そうでなければ、オーバーロード解決は失敗する。
6.2. プレーン型
プレーン型は、ブール値、数値、ベクトル、行列、またはそれらの値の集約の機械表現用の型です。
プレーン型は、スカラー型、アトミック型、 または合成型のいずれかです。
注: WGSLのプレーン型はC++のPlain-Old-Data型に似ていますが、 アトミック型や抽象数値型も含みます。
6.2.1. 抽象数値型
これらの型はWGSLソースで記述できません。型検査のみに使われます。
特定の式はシェーダー生成時に評価され、 GPUで直接実装される範囲や精度より大きい数値範囲・精度で評価される場合があります。
WGSLはこれらの評価のために、2つの抽象数値型を定義します:
-
AbstractInt 型は、符号ビットが最上位ビット位置にある64ビット2の補数形式で表現可能な整数の集合です。
-
AbstractFloat 型は、IEEE-754 binary64(倍精度)形式で表現可能な有限浮動小数点数の集合です。
これらの型で式を評価する場合、オーバーフローや無限大・NaN値を生成してはなりません。
型は、抽象数値型であるか抽象数値型を含んでいれば抽象型です。 そうでなければ具象型です。
サフィックスのない数値リテラルは、抽象数値型の値を表します:
-
整数リテラルに
i
やu
のサフィックスがない場合は、AbstractInt値を表します。 -
浮動小数点リテラルに
f
やh
のサフィックスがない場合は、AbstractFloat値を表します。
例:式log2(32)
の解析:
-
log2(32)
はlog2
組み込み関数への関数呼び出しと解釈され、オペランドはAbstractInt値32です。 -
log2
に整数スカラー型の仮引数を持つオーバーロードはありません。 -
代わりにオーバーロード解決が適用され、3つの候補オーバーロードと可能な自動変換が考慮されます:
-
AbstractIntからAbstractFloatへの変換(ランク4)
-
AbstractIntからf32への変換(ランク5)
-
AbstractIntからf16への変換(ランク6)
-
-
最終的な計算はAbstractFloat型(例:
log2(32.0)
)で行われます。
例:式1 + 2.5
の解析:
-
1 + 2.5
は、部分式がAbstractInt値1とAbstractFloat値2.5の加算演算と解釈されます。 -
eが整数型・fが浮動小数点型のe+fのオーバーロードはありません。
-
しかし、可能な自動変換を使うことで3つの候補オーバーロードがあります:
-
1
はAbstractFloat値1.0
(ランク4)に変換され、2.5
はそのままAbstractFloat(ランク0)です。
-
-
最初のオーバーロードが優先候補となり、型チェックは成功します。
-
最終的な計算はAbstractFloat型
1.0 + 2.5
として行われます。
例:let x = 1 + 2.5;
-
この例は上記と似ていますが、
x
は抽象数値型に解決できません。 -
宣言の効果は、
let x : f32 = 1.0f + 2.5f;
と書いた場合と同じです。
例:1u + 2.5
はシェーダー生成エラーとなります:
-
1u
項はu32型の式です。 -
2.5
項はAbstractFloat型の式です。 -
有効なオーバーロード候補はありません:
例:-1 * i32(-2147483648)
はシェーダー生成エラーになりません:
-
-1
項はAbstractInt型の式です。 -
i32(-2147483648)
項はi32型の式です。 -
これら2型に対する乗算演算子のオーバーロードはなく、i32項をAbstractInt型に上位変換することもできません。
-
唯一の実現可能な自動変換はAbstractIntをi32型に変換することなので:
// 明示型付き符号なし整数リテラル var u32_1 = 1u ; // u32を保持 // 明示型付き符号付き整数リテラル var i32_1 = 1i ; // i32を保持 // 明示型付き浮動小数点リテラル var f32_1 = 1f ; // f32を保持 // 明示型付き符号なし整数リテラルは負にできない var u32_neg = - 1u ; // 無効:u32は単項マイナス非対応 // 具象型が必要だが文や式のどの部分も型を強制しない場合、整数リテラルはi32として解釈される: // let宣言の初期化式は構築可能型(またはポインター)でなければならない。 // AbstractInt→構築可能型の自動変換で最優先(変換ランク2)はAbstractInt→i32。よって'1'はi32型になる。 let some_i32 = 1 ; // 例:let some_i32: i32 = 1i; // 宣言型から推論 var i32_from_type : i32= 1 ; // i32を保持。AbstractInt→i32、変換ランク2 var u32_from_type : u32= 1 ; // u32を保持。AbstractInt→u32、変換ランク3 // サフィックス無し整数リテラルは必要なら浮動小数点に変換可能: // AbstractInt→f32で自動変換、変換ランク5。 var f32_promotion : f32= 1 ; // f32を保持 // 無効:浮動小数点→整数への実現可能な変換はない var i32_demotion : i32= 1.0 ; // 無効 // 式から推論 var u32_from_expr = 1 + u32_1 ; // u32を保持 var i32_from_expr = 1 + i32_1 ; // i32を保持 // 値は表現可能でなければならない let u32_too_large : u32= 1234567890123456890 ; // 無効、オーバーフロー let i32_too_large : i32= 1234567890123456890 ; // 無効、オーバーフロー let u32_large : u32= 2147483649 ; // 有効 let i32_large : i32= 2147483649 ; // 無効、オーバーフロー let f32_out_of_range1 = 0x1p500 ; // 無効、範囲外 let f32_hex_lost_bits = 0x1.0000000001p0 ; // 無効、f32で正確に表現できない // 最小整数:AbstractIntに単項否定、型はi32に推論 // AbstractInt→構築可能型への最優先自動変換(最小変換ランク)はAbstractInt→i32 let i32_min = - 2147483648 ; // 型はi32 // 無効。AbstractInt→i32で型推論するが値が範囲外でシェーダー生成エラー let i32_too_large_2 = 2147483648 ; // 無効 // 部分式はAbstractIntやAbstractFloatに解決可能。 // 次の例はすべて有効で変数値は6u。 var u32_expr1 = ( 1 + ( 1 + ( 1 + ( 1 + 1 )))) + 1u ; var u32_expr2 = 1u + ( 1 + ( 1 + ( 1 + ( 1 + 1 )))); var u32_expr3 = ( 1 + ( 1 + ( 1 + ( 1u + 1 )))) + 1 ; var u32_expr4 = 1 + ( 1 + ( 1 + ( 1 + ( 1u + 1 )))); // 組み込み関数パラメータによる型推論 // 最優先候補はclamp(i32,i32,i32)->i32 let i32_clamp = clamp ( 1 , - 5 , 5 ); // 最優先候補はclamp(u32,u32,u32)。リテラルはAbstractInt→u32へ自動変換 let u32_clamp = clamp ( 5 , 0 , u32_from_expr ); // 最優先候補はclamp(f32,f32,f32)->f32。リテラルはAbstractInt→f32へ自動変換 let f32_clamp = clamp ( 0 , f32_1 , 1 ); // 次の例はすべてf32に昇格し、初期値は10f let f32_promotion1 = 1.0 + 2 + 3 + 4 ; let f32_promotion2 = 2 + 1.0 + 3 + 4 ; let f32_promotion3 = 1f + (( 2 + 3 ) + 4 ); let f32_promotion4 = (( 2 + ( 3 + 1f )) + 4 ); // 型規則違反 // 無効:初期化子はf32にしか解決できない // AbstractFloat→u32への自動変換は不可 let mismatch : u32= 1.0 ; // 無効:clampは符号混在引数のオーバーロードがない let ambiguous_clamp = clamp ( 1u , 0 , 1i ); // 推論は文レベルで完了 // let宣言の初期化式は構築可能型(またはポインター)でなければならない。 // AbstractInt→構築可能型への最優先自動変換(最小変換ランク)はAbstractInt→i32。よって'1'はi32型になる。 let some_i32 = 1 ; // 例:let some_i32: i32 = 1i; let some_f32 : f32= some_i32 ; // 型エラー:i32はf32に代入不可 // もう1つのオーバーフロー例 let overflow_u32 = ( 1 - 2 ) + 1u ; // 無効、-1はu32の範囲外 // 理想値は32ビット範囲外だが再び範囲内へ let out_and_in_again = ( 0x1ffffffff / 8 ); // 類似だが無効 let out_of_range = ( 0x1ffffffff / 8u ); // 計算は32ビットで行われ、 // 0x1ffffffffは範囲外
6.2.2. ブール型
bool型はtrue
とfalse
の値を持ちます。
前提条件 | 結論 | 説明 |
---|---|---|
true : bool
| true値 | |
false : bool
| false値 |
6.2.3. 整数型
u32型は32ビット符号なし整数の集合です。
i32型は32ビット符号付き整数の集合です。 2の補数表現を用い、符号ビットは最上位ビット位置にあります。
整数型(具象型)の式でオーバーフローが発生した場合、結果は2bitwidthで剰余を取った値になります。
型 | 最小値 | 最大値 |
---|---|---|
i32 | i32(-2147483648) | 2147483647i |
i32(-0x80000000) | 0x7fffffffi | |
u32 | 0u | 4294967295u |
0x0u | 0xffffffffu |
注: AbstractIntも整数型です。
6.2.4. 浮動小数点型
f32型は IEEE-754 binary32(単精度)形式の32ビット浮動小数点値の集合です。 詳細は§ 15.7 浮動小数点評価を参照してください。
f16型は
IEEE-754 binary16(半精度)形式の16ビット浮動小数点値の集合です。シェーダー生成エラー
となるのは、プログラムにenable f16;
ディレクティブが含まれていない場合にf16型を使用した場合です。
詳細は§ 15.7 浮動小数点評価を参照してください。
以下の表は浮動小数点型の極値を示します。各値には対応する負の値も存在します。
型 | 最小正の非正規化数 | 最小正の正規化数 | 最大正の有限値 | 最大の有限2のべき乗 |
---|---|---|---|---|
f32 | 1.40129846432481707092e-45f | 1.17549435082228750797e-38f | 3.40282346638528859812e+38f | 0x1p+127f |
0x1p-149f | 0x1p-126f | 0x1.fffffep+127f | ||
f16 | 5.9604644775390625e-8h | 0.00006103515625h | 65504.0h | 0x1p+15h |
0x1p-24h | 0x1p-14h | 0x1.ffcp+15h |
注: AbstractFloatも浮動小数点型です。
6.2.5. スカラー型
スカラー型は、bool、AbstractInt、 AbstractFloat、i32、u32、f32、f16です。
数値スカラー型は、AbstractInt、 AbstractFloat、i32、u32、f32、f16です。
整数スカラー型は、AbstractInt、i32、u32です。
スカラー変換は、あるスカラー型の値を別のスカラー型の値へ写像します。 一般に、変換後の値は変換前の値に近く、変換先型の制限内で表現されます。 スカラー変換は次の場合に発生します:
-
値コンストラクター組み込み関数を明示的に呼び出す場合
6.2.6. ベクトル型
ベクトルは2, 3, 4個のスカラー 要素をまとめたものです。
型 | 説明 |
---|---|
vecN<T> | T型の要素をN個持つベクトル。 Nは{2, 3, 4}のいずれかであり、Tは必ずスカラー型でなければなりません。 Tはベクトルの要素型です。 |
ベクトルの要素型が数値スカラーであれば、そのベクトルは数値ベクトルです。
ベクトルの主な利用例:
-
方向と大きさの両方を表現する
-
空間中の位置を表現する
-
色空間で色を表現する 例えば、要素は赤・緑・青の強度で、4つ目の要素はアルファ(不透明度)値となることがあります。
ベクトル(および行列)に対する多くの操作は 要素ごとに行われます。つまり、各スカラー要素ごとに独立して操作します。
let x : vec3< f32> = a + b ; // a, bはvec3<f32> // x[0] = a[0] + b[0] // x[1] = a[1] + b[1] // x[2] = a[2] + b[2]
事前宣言エイリアス | 元型 | 制限 |
---|---|---|
vec2i | vec2<i32> | |
vec3i | vec3<i32> | |
vec4i | vec4<i32> | |
vec2u | vec2<u32> | |
vec3u | vec3<u32> | |
vec4u | vec4<u32> | |
vec2f | vec2<f32> | |
vec3f | vec3<f32> | |
vec4f | vec4<f32> | |
vec2h | vec2<f16> | f16拡張が必要。 |
vec3h | vec3<f16> | |
vec4h | vec4<f16> |
6.2.7. 行列型
行列は2, 3, 4個の浮動小数点ベクトルをまとめたものです。
型 | 説明 |
---|---|
matCxR<T> | C列×R行、型Tの行列。C, Rは{2, 3, 4}のいずれかで、Tはf32、f16、AbstractFloatのいずれかでなければなりません。 同等に、C個のvecR<T>型の列ベクトルと考えることもできます。 |
行列の主な用途は線形変換の表現です。 この解釈では、行列のベクトルは列ベクトルとして扱われます。
乗算演算子(*
)は以下の用途で使われます:
-
変換をスカラーの大きさでスケールする
-
ベクトルに変換を適用する
-
他の行列と変換を合成する
§ 8.7 算術式を参照してください。
事前宣言エイリアス | 元型 | 制限 |
---|---|---|
mat2x2f | mat2x2<f32> | |
mat2x3f | mat2x3<f32> | |
mat2x4f | mat2x4<f32> | |
mat3x2f | mat3x2<f32> | |
mat3x3f | mat3x3<f32> | |
mat3x4f | mat3x4<f32> | |
mat4x2f | mat4x2<f32> | |
mat4x3f | mat4x3<f32> | |
mat4x4f | mat4x4<f32> | |
mat2x2h | mat2x2<f16> | f16拡張が必要。 |
mat2x3h | mat2x3<f16> | |
mat2x4h | mat2x4<f16> | |
mat3x2h | mat3x2<f16> | |
mat3x3h | mat3x3<f16> | |
mat3x4h | mat3x4<f16> | |
mat4x2h | mat4x2<f16> | |
mat4x3h | mat4x3<f16> | |
mat4x4h | mat4x4<f16> |
6.2.8. アトミック型
アトミック型は、 具象整数スカラー型をカプセル化し、以下の特徴を持ちます:
-
アトミックオブジェクトはある保証を並行する観測者に提供する
-
アトミックオブジェクトに対して有効な操作はatomic組み込み関数のみ
型 | 説明 |
---|---|
atomic<T> | 型Tのアトミック。Tは必ずu32またはi32でなければなりません。 |
式がアトミック型に評価されてはなりません。
アトミック型は、workgroup
アドレス空間の変数、またはstorage
buffer変数(read_writeアクセスモード)でのみインスタンス化可能です。
型への操作のメモリスコープは、その型がインスタンス化されるアドレス空間で決まります。
workgroup空間のアトミック型はWorkgroup
スコープ、
storage空間のアトミック型はQueueFamily
スコープです。
アトミック変更とは、 アトミックオブジェクトに対して値を書き換える操作のことです。 新しい値が既存値と同じ場合でも「変更」とみなします。
WGSLでは、アトミック変更は各オブジェクトごとに相互順序付けされます。 つまり、シェーダーステージの実行中、アトミックオブジェクトAごとに、 すべてのエージェントがAに適用された変更操作の順序を同じように観測します。 別個のアトミックオブジェクト間の順序は関連付けられません。因果関係も暗示されません。 workgroup空間の変数は同一workgroup内で共有されますが、 異なるworkgroup間では共有されません。
6.2.9. 配列型
配列は要素値をインデックスでアクセスできる並びです。
型 | 説明 |
---|---|
array<E,N> | 固定長配列。E型の要素をN個持つ。 Nは配列の要素数です。 |
array<E> | 実行時サイズ配列。E型の要素からなる配列。
特定の文脈でのみ現れます。 |
配列の最初の要素はインデックス0、以降は整数インデックスでアクセスします。 § 8.5.3 配列アクセス式参照。
式が実行時サイズ配列型に評価されてはなりません。
固定長配列の要素数式Nには以下の制約があります:
-
Nが0以下の場合:
-
Nが定数式ならシェーダー生成エラー
-
それ以外の場合はパイプライン生成エラー
-
注: 要素数値がoverride宣言に依存する場合はパイプライン生成時、 それ以外はシェーダーモジュール生成時に完全に決まります。
注: 型等価性判定のため、定数式でないoverride式は識別子でなければなりません。 オーバーライド可能定数でサイズ指定したworkgroup変数例参照。
実行時サイズ配列の要素数は、対応するstorage buffer変数に関連付けられたバッファバインディングのサイズで決まります。 § 13.3.4 バッファバインディングによる実行時サイズ配列の要素数決定参照。
配列要素型は、以下のいずれかでなければなりません:
-
スカラー型
-
ベクトル型
-
行列型
-
配列型(生成時固定フットプリントを持つ)
-
構造体型(生成時固定フットプリントを持つ)
注: 要素型はプレーン型でなければなりません。
2つの配列型が同じかどうかは、以下すべてが満たされた場合のみです:
-
同じ要素型を持つ
-
要素数指定が一致する。すなわち、以下のいずれかが成り立つ:
-
両者とも実行時サイズ
-
両者とも生成時固定フットプリント付き固定長で、要素数が同値(符号違いでも可。要素数は常に正なので比較可能)
-
両者とも識別子で指定されており、同じパイプラインオーバーライド可能定数宣言に解決される
-
// array<f32,8>とarray<i32,8>は型が異なる:要素型が違う var < private> a : array< f32, 8 > ; var < private> b : array< i32, 8 > ; var < private> c : array< i32, 8u > ; // array<i32,8>とarray<i32,8u>は同じ型 const width = 8 ; const height = 8 ; // array<i32,8>、array<i32,8u>、array<i32,width>は同じ型。要素数評価は8 var < private> d : array< i32, width > ; // array<i32,height>とarray<i32,width>は同じ型 var < private> e : array< i32, width > ; var < private> f : array< i32, height > ;
注: オーバーライド可能定数でサイズ指定した配列型の有効な利用は、 workgroupアドレス空間でのメモリビューとしてのみです。 これはworkgroup変数の格納型を含みます。 § 7 変数と値の宣言参照。
override blockSize = 16 ; var < workgroup> odds : array< i32, blockSize > ; var < workgroup> evens : array< i32, blockSize > ; // 同じ型 // 以下はすべて'odds'および'evens'と型が異なる // 違う型:識別子'blockSize'でない var < workgroup> evens_0 : array< i32, 16 > ; // 違う型:算術式で要素数指定 var < workgroup> evens_1 : array< i32,( blockSize * 2 / 2 ) > ; // 違う型:括弧付き識別子 var < workgroup> evens_2 : array< i32,( blockSize ) > ; // 無効例:オーバーライド要素数は最外部のみ // var<workgroup> both: array<array<i32,blockSize>,2>; // 無効例:オーバーライド要素数はworkgroup変数でのみ有効 // var<private> bad_address_space: array<i32,blockSize>;
6.2.10. 構造体型
構造体は、名前付きメンバー値を名前付きでまとめたものです。
型 | 説明 |
---|---|
struct AStructName {M1 : T1, ... MN : TN, } |
識別子AStructNameで名前付けされた構造体型の宣言で、
N個のメンバーを持ちます。
メンバーiは識別子Miで名前付けされ、
型はTiです。
Nは必ず1以上でなければなりません。 同じ構造体型内の2つのメンバーは同じ名前であってはなりません。 |
構造体型はモジュールスコープで宣言されます。 プログラムソースの他の場所では、構造体型はその識別子名で示されます。 § 5 宣言とスコープ参照。
2つの構造体型は名前が同じ場合のみ同じ型です。
構造体メンバー型は、以下のいずれかでなければなりません:
-
スカラー型
-
ベクトル型
-
行列型
-
固定長配列型(生成時固定フットプリント付き)
-
実行時サイズ配列型(ただし構造体の最後のメンバーのみ)
-
構造体型(生成時固定フットプリント付き)
注: ユーザー宣言構造体型はすべて具象型です。
注: 各メンバー型はプレーン型でなければなりません。
構造体メンバー型および配列要素型の制約により生じる結果:
-
ポインター、テクスチャ、サンプラーは、いかなる入れ子レベルでも配列や構造体内に現れてはなりません。
-
実行時サイズ配列が複合型の一部の場合、構造体の最後の要素としてのみ現れ、 その構造体自体はさらに配列や構造体に含まれてはなりません。
// 3つのメンバーを持つ構造体 struct Data { a : i32, b : vec2< f32> , c : array< i32, 10 > , // 最後のカンマは省略可能 } // Data型値を格納する変数を宣言 var < private> some_data : Data ;
'struct'
ident struct_body_decl
'{'
struct_member ( ','
struct_member ) * ','
? '}'
構造体メンバーに適用できる属性:
属性builtin、location、blend_src、 interpolate、 invariantは IO属性です。 構造体SのメンバーにIO属性がある場合、Sが仮引数や戻り値型としてエントリーポイントで使われる場合のみ効果があります。 § 13.3.1 ステージ間入出力インターフェース参照。
属性alignとsizeはレイアウト属性であり、 構造体型がユニフォームバッファやストレージバッファを定義する際に必要となる場合があります。 § 14.4 メモリレイアウト参照。
// 実行時配列 alias RTArr = array< vec4< f32>> ; struct S { a : f32, b : f32, data : RTArr } @group ( 0 ) @binding ( 0 ) var < storage> buffer : S ;
6.2.11. 合成型
型は、他の型を合成して内部構造を持つ場合合成型です。 内部部分は重複せず、構成要素と呼ばれます。 合成値は構成要素に分解可能です。§ 8.5 合成値分解式参照。
合成型は以下の通りです:
合成型Tについて、入れ子深度(NestDepth(T))は:
-
ベクトル型なら1
-
行列型なら2
-
要素型Eの配列型なら 1 + NestDepth(E)
-
メンバー型M1,...,MNを持つ構造体型Tなら 1 + max(NestDepth(M1),..., NestDepth(MN))
6.2.12. 構築可能型
多くの種類の値は生成、ロード、格納、関数への渡し、関数からの返却が可能です。 これらを構築可能と呼びます。
型が次のいずれかであれば、構築可能です:
注: すべての構築可能型は生成時固定フットプリントを持ちます。
注: アトミック型や実行時サイズ配列型は構築可能ではありません。 アトミックや実行時サイズ配列を含む複合型も構築可能ではありません。
6.2.13. 固定フットプリント型
変数のメモリフットプリントは、その変数の内容を格納するために使われるメモリ位置の数です。 メモリフットプリントは変数の格納型に依存し、シェーダーライフサイクルのある段階で確定します。 ほとんどの変数は、シェーダー生成時にサイズが決まります。 一部の変数は、パイプライン生成時、 またはシェーダー実行開始時に決まることもあります。
型が生成時固定フットプリントを持つとは、その具象化のサイズがシェーダ生成時に完全に決定される場合を指します。
型が固定フットプリントを持つとは、そのサイズがパイプライン生成時に完全に決まる場合です。
注: すべての具象生成時固定フットプリント型・固定フットプリント型は格納可能型です。
注: パイプライン生成はシェーダー生成に依存するので、生成時固定フットプリントを持つ型は、必ず固定フットプリントも持ちます。
生成時固定フットプリントを持つ型は:
注: 構築可能型は生成時固定フットプリントを持ちます。
-
生成時固定フットプリントを持つ型
-
固定長配列型(要素数への追加制約なし)
注: 定数式でないoverride式による要素数指定の固定長配列型が有効なのは、 workgroupアドレス空間でのメモリビューのみです。 これはworkgroup変数の格納型も含みます。
注: 固定フットプリント型は直接または間接的にアトミック型を含み得ますが、構築可能型は含みません。
注: 固定フットプリント型には実行時サイズ配列、およびそれを含む構造体は含まれません。
6.3. 列挙型
列挙型は、限定された名前付き値の集合です。 列挙型は、例えば有効なテクセルフォーマットの集合など、特定の概念の可能性の集合を区別するために使われます。
列挙値は、列挙型内の名前付き値のひとつです。 各列挙値は他のすべての列挙値、および他の種類の値と区別されます。
WGSLソースで新しい列挙値や列挙型を宣言する方法はありません。
注: 列挙値はテンプレートパラメータとして使われます。
6.3.1. 事前宣言列挙値
次の表はWGSLの列挙型と、その事前宣言列挙値を示します。 列挙型自体はWGSLソースで記述できません。
列挙型 (WGSLで記述不可) | 事前宣言列挙値 |
---|---|
アクセスモード | read |
write | |
read_write | |
アドレス空間
注:
| function |
private | |
workgroup | |
uniform | |
storage | |
テクセルフォーマット | rgba8unorm |
rgba8snorm | |
rgba8uint | |
rgba8sint | |
rgba16unorm | |
rgba16snorm | |
rgba16uint | |
rgba16sint | |
rgba16float | |
rg8unorm | |
rg8snorm | |
rg8uint | |
rg8sint | |
rg16unorm | |
rg16snorm | |
rg16uint | |
rg16sint | |
rg16float | |
r32uint | |
r32sint | |
r32float | |
rg32uint | |
rg32sint | |
rg32float | |
rgba32uint | |
rgba32sint | |
rgba32float | |
bgra8unorm | |
r8unorm | |
r8snorm | |
r8uint | |
r8sint | |
r16unorm | |
r16snorm | |
r16uint | |
r16sint | |
r16float | |
rgb10a2unorm | |
rgb10a2uint | |
rg11b10ufloat |
6.4. メモリビュー
WGSLプログラムはプレーン値の計算に加え、 メモリから値を読み書きすることも多く、これはメモリアクセス操作によって行われます。 各メモリアクセスはメモリビューを介して実行されます。
メモリビューは以下から構成されます:
メモリビューのアクセスモードは、アドレス空間でサポートされていなければなりません。§ 7 変数と値の宣言参照。
6.4.1. 格納可能型
変数に格納される値は必ず 格納可能型でなければなりません。 格納可能型はWGSLで明示的な表現が定義されている場合もあり(§ 14.4.4 値の内部レイアウト参照)、 テクスチャやサンプラーのように不透明な場合もあります。
型が具象型で、以下のいずれかであれば格納可能型です:
注: つまり、格納可能型は具象プレーン型、テクスチャ型、サンプラー型です。
6.4.2. ホスト共有型
ホスト共有型は、ホストとGPU間で共有されるバッファの内容を記述する型や、フォーマット変換せずにホストとGPU間でコピーされる型です。 この目的で使う場合、型にはレイアウト属性が適用されることがあります(§ 14.4 メモリレイアウト参照)。 § 7.3 var宣言の通り、ユニフォームバッファやストレージバッファ 変数の格納型は必ずホスト共有型でなければなりません。
型が具象型で、以下のいずれかであればホスト共有型です:
-
行列型
-
固定長配列型(生成時固定フットプリントがあり、要素型がホスト共有型の場合)
-
実行時サイズ配列型(要素型がホスト共有型の場合)
-
すべてのメンバーがホスト共有型の構造体型
注: ステージ間入出力型の制約は§ 13.3.1 ステージ間入出力インターフェースおよびそれ以降の節に記載されています。 それらの型もサイズが決まりますが、カウント方法が異なります。
注: テクスチャやサンプラーもホストとGPU間で共有できますが、その内容は不透明です。 この節のホスト共有型は、特にストレージおよびユニフォームバッファで使います。
6.4.3. 参照型とポインター型
WGSLにはメモリビューを表現する型が2種類あります: 参照型とポインター型です。
制約 | 型 | 説明 |
---|---|---|
ASはアドレス空間、 Tは格納可能型、 AMはアクセスモード | ref<AS,T,AM> |
参照型
であり、AS内のメモリ位置でT型の値を保持し、
AMモードによるメモリアクセスをサポートするメモリビューの集合に対応します。
ここでTは格納型です。 参照型はWGSLソースでは書かれません。モジュール解析時に使われます。 |
ASはアドレス空間、 Tは格納可能型、 AMはアクセスモード | ptr<AS,T,AM> |
ポインター型
であり、AS内のメモリ位置でT型の値を保持し、
AMモードによるメモリアクセスをサポートするメモリビューの集合に対応します。
ここでTは格納型です。 ポインター型はWGSLソースで記述可能です。 |
2つのポインター型は、アドレス空間・格納型・アクセスモードがすべて同じ場合のみ同じ型です。
WGSLモジュール解析時、参照型・ポインター型はアドレス空間・格納可能型・アクセスモードで完全にパラメータ化されます。 本仕様のコード例では、コメントでこの完全パラメータ化形を示します。
ただしWGSLソーステキストでは:
-
参照型は現れてはなりません。
-
ポインター型は現れることがあります。
fn my_function ( /* 'ptr<function,i32,read_write>'は、'function'アドレス空間のメモリ位置でi32値を保持する ポインター値の型です。ここで'i32'は格納型です。 アクセスモードは暗黙で'read_write'です。 デフォルトは「アドレス空間」節参照。 */ ptr_int : ptr< function, i32> , // 'ptr<private,array<f32,50>,read_write>'は、'private'アドレス空間のメモリ位置で // f32型50要素の配列を保持するポインター値の型です。 // 格納型は'array<f32,50>'。 // アクセスモードは暗黙で'read_write'。 // デフォルトは「アドレス空間」節参照。 ptr_array : ptr< private, array< f32, 50 >> ) { }
参照型・ポインター型はいずれもメモリビューの集合であり、特定のメモリビューは一意な参照値と一意なポインター値に対応します:
ptr<AS,T,AM型の各ポインター値pは、ref<AS,T,AM型の一意な参照値rに対応し、 逆も同様で、pとrは同じメモリビューを表します。
6.4.4. 有効・無効なメモリ参照
参照は§ 6.4.8 参照とポインター値の形成で詳しく説明されているように形成されます。 一般に、有効な参照は次の方法で形成されます:
-
変数を名前で指定する
一般に、無効なメモリ参照は次の方法で形成されます:
-
間接参照演算子を無効なポインターに適用する
有効なポインターは有効な参照に対応するポインターです。 無効なポインターは無効なメモリ参照に対応するポインターです。
6.4.5. 起源変数
ポインター値の起源変数は、対応する参照値の起源変数として定義されます。
注: 起源変数は動的な概念です。 関数の仮引数の起源変数は、その関数の呼び出し元によります。 呼び出し元が異なれば、異なる起源変数へのポインターが渡されることがあります。
有効な参照は必ず、ある変数のメモリ位置の一部または全部に対応する非空のメモリビューとなります。
次の例では、参照the_particle.position[i]
は、i
が0または1のときのみ有効です。
i
が2の場合、その参照は無効なメモリ参照ですが、参照先はthe_particle.color_index
のメモリ位置となります。
6.4.6. 範囲外アクセス
メモリアクセス操作が無効なメモリ参照をアクセスすると、範囲外アクセスとなります。
範囲外アクセスはプログラム不具合です。もしそのまま実行された場合、一般的には:
このため、実装は決してそのままアクセスを実行しません。 範囲外アクセスの実行は動的エラーとなります。
注: 格納型の誤解釈例は
前節の例で発生します。
i
が2の場合、the_particle.velocity[i]
式は
型ref<storage,f32,read_write>
(格納型はf32)ですが、
実際のメモリ位置はcolor_index
メンバー用に割り当てられているため、値はi32型です。
その結果には以下が含まれます(限定されません):
- トラップ
-
シェーダー呼び出しは直ちに終了し、シェーダーステージ出力は0値となる。
- 無効ロード
-
無効参照からのロードは次のいずれかを返す:
- 無効ストア
-
無効参照へのストアは次のいずれか:
共有アドレス空間内の変数の別の位置へ無効なロードやストアがリダイレクトされた場合、データ競合が発生する可能性があります。 例えば、複数の並行実行呼び出しが配列の最初の要素へリダイレクトされ、少なくとも1つが書き込みなら同期されていない限りデータ競合=動的エラーとなります。
範囲外アクセスは均一性解析の前提を破棄します。 例えば、範囲外アクセスで呼び出しが早期終了すると、集合演算に参加できなくなります。 特にworkgroupBarrier呼び出しがシェーダーをハングさせたり、微分値が不正になることがあります。
6.4.7. 参照型とポインター型の利用例
参照型とポインター型は用途で区別されます:
-
変数の型は参照型
-
アドレス演算(単項
&
)は参照値を対応するポインター値に変換する -
間接参照(単項
*
)はポインター値を対応する参照値に変換する -
let宣言はポインター型にできるが参照型にはできない
-
関数仮引数はポインター型にできるが参照型にはできない
-
ロード規則:関数内では参照型は型規則を満たすため自動的にデリファレンスされる(値が読み出される):
-
関数内で、格納型Tの参照式rが文や式で使われ、
-
rのアクセスモードがreadまたはread_writeで、
-
一致する型規則がrに型Tの値を要求する場合、
-
その型規則の要求は満たされたものとみなされる
-
その状況でrを評価すると、参照先メモリ位置の値(型T)が得られる。つまり、読み出しアクセスが行われる。
-
このような参照型定義により、変数の簡便な利用が可能となります:
@compute @workgroup_size ( 1 ) fn main () { // 'i'はref<function,i32,read_write>型 // 'i'のメモリ位置にはi32値0が格納されている。 var i : i32= 0 ; // 'i + 1'は'i'部分式がi32型でなければ型規則に一致しない。 // よって'i + 1'式はi32型。評価時、'i'部分式はその時点でメモリ位置に格納されたi32値となる。 let one : i32= i + 1 ; // 'i'のメモリ位置値を2に更新 i = one + 1 ; // 'i'のメモリ位置値を5に更新。右辺は代入前に評価される。 i = i + 3 ; }
var < private> age : i32; fn get_age () -> i32{ // return文の式の型は'i32'でなければならない(関数の戻り値型に一致)。 // 'age'式は型ref<private,i32,read_write>。 // 参照の格納型が要求される型と一致し、他の型規則が適用されないため、Load規則を適用。 // この文脈での'age'評価はreturn文実行時に参照するメモリ領域からロードされたi32値。 return age ; } fn caller () { age = 21 ; // copy_age定数にはi32値21が代入される。 let copy_age : i32= get_age (); }
ポインター型定義により、2つの主要な用途が可能となります:
-
let宣言でポインター型を使い、変数内容の一部の短い名前を作る
-
関数の仮引数にポインター型を使い、呼び出し元関数からアクセス可能な変数のメモリを参照する
-
その関数呼び出しでは、そのオペランドにポインター値を渡す必要があり、通常は
&
アドレス演算で変数内容へのポインターを得る
-
struct Particle { position: vec3< f32> , velocity : vec3< f32> } struct System { active_index : i32, timestep : f32, particles : array< Particle , 100 > } @group ( 0 ) @binding ( 0 ) var < storage, read_write> system : System ; @compute @workgroup_size ( 1 ) fn main () { // ストレージメモリ内の特定Particleへのポインターを形成 let active_particle = & system . particles [ system . active_index ]; let delta_position : vec3< f32> = ( * active_particle ). velocity * system . timestep ; let current_position : vec3< f32> = ( * active_particle ). position; ( * active_particle ). position= delta_position + current_position ; }
fn add_one ( x : ptr< function, i32> ) { /* 'x' の場所に次の大きい整数値を格納するように更新する、 (もしくは、最大の負の i32 値にラップアラウンドする)。 左辺では、単項 '*' はポインタを参照に変換し、 それに代入できるようにする。デフォルトでは read_write アクセスモードを持つ。 /* 右辺について: - 単項 '*' はポインタを参照に変換し、read_write アクセスモードを持つ。 - 一致する型ルールは加算 (+) のみで、'*x' が i32 型である必要がある。 これは '*x' のストア型である。よってロードルールが適用され、 評価時点で '*x' に格納されている値、すなわち i32 の 0 となる。 - 0 に 1 を加算し、右辺の最終値として 1 になる。*/ 1 を '*x' のメモリに格納する。*/ * x = * x + 1 ; } @compute @workgroup_size ( 1 ) fn main () { var i : i32= 0 ; // 'i' の内容を修正し、1 にする。 // 単項 '&' を使い、'i' のポインタ値を取得する。 // 呼び出し先の関数が 'i' のメモリにアクセスでき、修正可能であることを明確に示す。 add_one ( & i ); let one : i32= i ; // 'one' の値は 1 となる。 }
6.4.8. 参照値とポインター値の生成
参照値は次のいずれかの方法で生成されます:
-
ポインターに間接参照(単項
*
)演算を適用する。 -
-
ストア型がベクトルの場合、1文字のベクトルアクセス句を追加すると、その成分への参照値になります。 § 8.5.1.3 ベクトルメモリビューからの成分参照参照。
-
ストア型が構造体の場合、メンバーアクセス句を追加すると、そのメンバーへの参照値になります。 § 8.5.4 構造体アクセス式参照。
-
-
-
ストア型がベクトルの場合、配列インデックスアクセス句を追加すると、その成分への参照値になります。 § 8.5.1.3 ベクトルメモリビューからの成分参照参照。
-
ストア型が行列の場合、配列インデックスアクセス句を追加すると、その列ベクトルへの参照値になります。 § 8.5.2 行列アクセス式参照。
-
ストア型が配列の場合、配列インデックスアクセス句を追加すると、その要素への参照値になります。 § 8.5.3 配列アクセス式参照。
-
全ての場合で、結果のアクセスモードは元の参照のものと同じです。
struct S { age : i32, weight : f32} var < private> person : S ; // 別の場所で'person'は変数の基底メモリへの参照を表し、型はref<private,S,read_write>となる。 fn f () { var uv : vec2< f32> ; // この関数本体では'uv'はその基底メモリへの参照を表し、型はref<function,vec2<f32>,read_write>となる。 // 代入の左辺を評価: // 'uv.x'を評価して参照値を得る: // 1. まず'uv'を評価し、変数'uv'のメモリへの参照(型ref<function,vec2<f32>,read_write>)を得る。 // 2. '.x'ベクトルアクセス句を適用し、参照値の指すベクトルの第1成分メモリへの参照(型ref<function,f32,read_write>)を得る。 // 右辺を評価するとf32値1.0となる。1.0をuv.xのストレージメモリ位置に格納。 uv . x = 1.0 ; // 代入の左辺を評価: // 'uv[1]'を評価して参照値を得る: // 1. まず'uv'を評価し、変数'uv'のメモリへの参照(型ref<function,vec2<f32>,read_write>)を得る。 // 2. '[1]'配列インデックス句を適用し、参照値の指すベクトルの第2成分メモリへの参照(型ref<function,f32,read_write>)を得る。 // 右辺はf32値2.0。2.0をuv[1]のストレージメモリ位置に格納。 uv [ 1 ] = 2.0 ; var m : mat3x2< f32> ; // 'm[2]'の評価: // 1. 'm'を評価し、変数'm'のメモリへの参照(型ref<function,mat3x2<f32>,read_write>)を得る。 // 2. '[2]'配列インデックス句を適用し、参照値の指す行列の第3列ベクトルメモリへの参照(型ref<function,vec2<f32>,read_write>)を得る。 // let宣言はvec2<f32>型なので、初期化子はvec2<f32>型でなければならない。 // ロード規則が適用され、'm[2]'参照位置からロードした値が初期化子となる。 let p_m_col2 : vec2< f32> = m [ 2 ]; var A : array< i32, 5 > ; // 'A[4]'の評価: // 1. 'A'を評価し、変数'A'のメモリへの参照(型ref<function,array<i32,5>,read_write>)を得る。 // 2. '[4]'配列インデックス句を適用し、参照値の指す配列の第5要素メモリへの参照(型ref<function,i32,read_write>)を得る。 // let宣言はi32型なので、右辺はi32型でなければならない。 // ロード規則が適用され、'A[4]'参照位置からロードした値が初期化子となる。 let A_4_value : i32= A [ 4 ]; // 'person.weight'の評価: // 1. 'person'を評価し、モジュールスコープ変数のメモリへの参照(型ref<private,S,read_write>)を得る。 // 2. '.weight'メンバーアクセス句を適用し、参照値の指すメモリの第2メンバー位置への参照(型ref<private,f32,read_write>)を得る。 // let宣言はf32型なので、右辺はf32型でなければならない。 // ロード規則が適用され、'person.weight'参照位置からロードした値が初期化子となる。 let person_weight : f32= person . weight ; // また、ポインターからも同じ構文で参照値が生成できる。 let uv_ptr = & uv ; // この関数本体では'uv_ptr'は'uv'基底メモリへのポインターで、型はptr<function,vec2<f32>,read_write>となる。 // 代入左辺を評価: // '*uv_ptr'を評価して参照値を得る: // 1. 'uv_ptr'を評価し、'uv'基底メモリへのポインター(型ptr<function,vec2<f32>,read_write>)を得る。 // 2. 間接参照演算子を適用し、'uv'のメモリへの参照を得る。 // 右辺はvec2<f32>値(1.0, 2.0)。'uv'のストレージメモリ位置に(1.0, 2.0)を格納。 * uv_ptr = vec2f( 1.0 , 2.0 ); // 代入左辺を評価: // 'uv_ptr.x'を評価して参照値を得る: // 1. 'uv_ptr'を評価し、'uv'基底メモリへのポインター(型ptr<function,vec2<f32>,read_write>)を得る。 // 2. '.x'ベクトルアクセス句を適用し、参照値の指すベクトルの第1成分メモリへの参照(型ref<function,f32,read_write>)を得る。 // 右辺はf32値1.0。uv.xのストレージメモリ位置に1.0を格納。 uv_ptr . x = 1.0 ; // 代入左辺を評価: // 'uv_ptr[1]'を評価して参照値を得る: // 1. 'uv_ptr'を評価し、'uv'基底メモリへのポインター(型ptr<function,vec2<f32>,read_write>)を得る。 // 2. '[1]'配列インデックス句を適用し、参照値の指すベクトルの第2成分メモリへの参照(型ref<function,f32,read_write>)を得る。 // 右辺はf32値2.0。uv[1]のストレージメモリ位置に2.0を格納。 uv_ptr [ 1 ] = 2.0 ; let m_ptr = & m ; // 'm_ptr[2]'の評価: // 1. 'm_ptr'を評価し、'm'基底メモリへのポインター(型ptr<function,mat3x2<f32>,read_write>)を得る。 // 2. '[2]'配列インデックス句を適用し、参照値の指す行列の第3列ベクトルへの参照(型ref<function,vec2<f32>,read_write>)を得る。 // let宣言はvec2<f32>型なので、初期化子はvec2<f32>型でなければならない。 // ロード規則が適用され、'm[2]'参照位置からロードした値が初期化子となる。 let p_m_col2 : vec2< f32> = m_ptr [ 2 ]; let A_ptr = & A ; // 'A[4]'の評価: // 1. 'A_ptr'を評価し、'A'基底メモリへのポインター(型ptr<function,array<i32,5>,read_write>)を得る。 // 2. '[4]'配列インデックス句を適用し、参照値の指す配列の第5要素メモリへの参照(型ref<function,i32,read_write>)を得る。 // let宣言はi32型なので、右辺はi32型でなければならない。 // ロード規則が適用され、'A[4]'参照位置からロードした値が初期化子となる。 let A_4_value : i32= A_ptr [ 4 ]; let person_ptr = & person ; // 'person.weight'の評価: // 1. 'person_ptr'を評価し、モジュールスコープ変数のメモリへのポインター(型ptr<private,S,read_write>)を得る。 // 2. '.weight'メンバーアクセス句を適用し、参照値の指すメモリの第2メンバー位置への参照(型ref<private,f32,read_write>)を得る。 // let宣言はf32型なので、右辺はf32型でなければならない。 // ロード規則が適用され、'person.weight'参照位置からロードした値が初期化子となる。 let person_weight : f32= person_ptr . weight ; }
ポインター値は次のいずれかの方法で生成されます:
全ての場合で、結果のアクセスモードは元のポインターのものと同じです。
// privateアドレス空間にf32値を格納する変数を宣言 var < private> x : f32; fn f () { // functionアドレス空間にi32値を格納する変数を宣言 var y : i32; // 'x'はモジュールスコープ変数'x'に解決され、型はref<private,f32,read_write>となる。 // 単項'&'演算で参照をポインターに変換。 // アクセスモードは元変数のものと同じ。完全型はptr<private,f32,read_write>。 // functionアドレス空間のデフォルトアクセスモードはread_writeなので明記不要。 let x_ptr : ptr< private, f32> = & x ; // 'y'は関数スコープ変数'y'に解決され、型はref<private,i32,read_write>となる。 // 単項'&'演算で参照をポインターに変換。アクセスモードはデフォルトでread_write。 let y_ptr : ptr< function, i32> = & y ; // 新しい変数。モジュールスコープ変数と別物。 var x : u32; // ここで'x'は直前に宣言された関数スコープ変数で、型はref<function,u32,read_write>。 // 単項'&'演算で参照をポインターに変換。アクセスモードはデフォルトでread_write。 let inner_x_ptr : ptr< function, u32> = & x ; }
6.4.9. 他言語における参照型・ポインター型との比較
この節は参考情報であり、規範的ではありません。
WGSLの参照型・ポインター型は他言語よりも制限されています。特に:
-
WGSLでは参照型を別の参照や変数へのエイリアスとして直接宣言できません(変数・仮引数いずれも)。
-
WGSLではポインター型・参照型は格納可能型ではありません。 すなわち、WGSLの変数宣言の内容としてポインターや参照を格納できません。
-
WGSLでは関数がポインター型や参照型を返してはなりません。
-
WGSLでは整数値とポインター値の間で相互変換する方法がありません。
-
WGSLではポインター値の型を強制的に別のポインター型に変換する方法がありません。
-
合成成分参照式は異なります。これは合成値への参照から、合成値内部の構成要素・要素への参照を生成します。 WGSLではこれらは異なる参照とみなされ、実装の抽象度が低い場合は同じ機械アドレスでも異なります。
-
-
WGSLでは参照値の型を強制的に別の参照型に変換する方法がありません。
-
WGSLではポインター型や参照型のアクセスモードを変更する方法がありません。
-
比較として、C++では非constポインターを自動でconstポインターに変換し、const値を非const値に変換する
const_cast
があります。
-
-
WGSLでは「ヒープ」から新たなメモリ領域を割り当てる方法がありません。
-
WGSLでは変数の明示的な破棄方法がありません。WGSL変数のメモリはスコープを抜けたときのみアクセス不可になります。
注: 上記規則から「ダングリング」ポインター、すなわち「生存」起源変数のメモリを参照しないポインターを生成することはできません。 メモリビューは無効なメモリ参照となる場合がありますが、 決して 起源変数またはバッファに関連しないメモリ位置へアクセスしません。
6.5. テクスチャ型・サンプラー型
テクセルはテクスチャの最小単位として個別にアクセス可能なスカラーまたはベクトルです。 texelはtexture elementの略です。
テクスチャは、レンダリングに有用な特殊な操作をサポートするテクセル集合です。 WGSLではこれらの操作はテクスチャ組み込み関数で呼び出します。 全リストは§ 17.7 テクスチャ組み込み関数参照。
WGSLのテクスチャはWebGPUのGPUTexture
に対応します。
テクスチャには以下の特徴があります:
- テクセルフォーマット
-
各テクセルのデータ表現。§ 6.5.1 テクセルフォーマット参照。
- 次元性
-
グリッド座標の次元数と座標の解釈方法。次元数は1, 2, 3のいずれかです。 多くのテクスチャは直交座標を使います。 キューブテクスチャは6つの正方形面を持ち、三次元座標は原点からキューブ中心への方向ベクトルとして解釈されます。
WebGPUの
GPUTextureViewDimension
参照。 - サイズ
-
各次元のグリッド座標の範囲。ミップレベルによって決まります。
- ミップレベル数
-
ミップレベル数はサンプルテクスチャや深度テクスチャでは1以上、ストレージテクスチャでは1です。
ミップレベル0がテクスチャのフルサイズ版を保持し、各次レベルは前レベルをフィルタした半分サイズ(切り上げ)です。
テクスチャをサンプリングする際、明示的または暗黙的な詳細度(LOD)でミップレベルを選択し、フィルタリングで合成値を生成します。 - 配列化
-
テクスチャが配列化されているかどうか。
-
非配列テクスチャはテクセルのグリッドです。
-
配列化テクスチャは同種グリッドの配列です。
-
- 配列サイズ
-
配列化テクスチャの場合、グリッド数。
- サンプル数
-
テクスチャがマルチサンプルの場合のサンプル数。
テクスチャ内の各テクセルには一意な論理テクセルアドレスが関連付けられており、これは整数タプルで:
テクスチャの物理的な構造は描画最適化のために設計されており、データレイアウトや型、内部処理はシェーダ言語から直接指定できません。
このため、シェーダはテクスチャ変数内のテクセルメモリに直接アクセスできません。 代わりに不透明なハンドル経由でアクセスします:
-
シェーダ内:
-
WebGPUパイプライン構築時、テクスチャ変数の格納型・バインディングは対応するバインドグループレイアウトエントリと互換でなければならない。
このように、テクスチャ型でサポートされる操作は、テクスチャ組み込み関数にそのテクスチャ型の仮引数が存在するかで決まります。
注: テクスチャ変数が持つハンドルはシェーダで変更できません。 すなわち、変数自体はread-onlyですが、アクセス先の基底テクスチャが可変(例:write-only ストレージテクスチャ)でも同様です。
テクスチャ型は以下の型です:
サンプラーはテクセルをサンプルテクスチャや深度テクスチャからアクセスする方法を制御する不透明なハンドルです。
WGSLサンプラーはWebGPUのGPUSampler
に対応します。
テクセルアクセスはサンプラーの複数のプロパティで制御されます:
- アドレッシングモード
-
テクスチャ境界・範囲外座標の処理方法を制御。 次元ごとに独立して設定可能。 WebGPU
GPUAddressMode
参照。 - フィルタモード
-
最終結果を得るためにどのテクセルを使うか制御。 最近傍テクセルまたは複数テクセル間で補間可。 複数フィルタモードは独立設定可能。 WebGPU
GPUFilterMode
参照。 - LODクランプ
-
アクセスする詳細度(LOD)の最小・最大値を制御。
- 比較
-
比較サンプラー用の比較方法を制御。 WebGPU
GPUCompareFunction
参照。 - 最大異方性
-
サンプラーで使用する最大異方性値を制御。
サンプラーはWGSLモジュール内で生成できず、上記プロパティ等の状態はシェーダ内では不変であり、WebGPU APIのみで設定可。
フィルタリングサンプラー(補間フィルタ使用サンプラー)を非フィルタ対応フォーマットのテクスチャで使うとパイプライン生成エラーとなります。
注: サンプラー変数が持つハンドルはシェーダで変更できません。
6.5.1. テクセルフォーマット
WGSLでは、特定のテクスチャ型がテクセルフォーマットによってパラメータ化されます。
テクセルフォーマットは、次の特徴を持ちます:
- チャンネル
-
各チャンネルはスカラー値を含みます。 テクセルフォーマットは最大4つのチャンネル(
r
、g
、b
、a
)を持ち、 通常は赤・緑・青・アルファチャンネルの概念に対応します。 - チャンネルフォーマット
-
チャンネルのビット数とそのビットの解釈方法。
WGSLの各テクセルフォーマットは、同名のWebGPU GPUTextureFormat
に対応します。
WGSLソースコードで使われるのは限られたテクセルフォーマットのみです。 それらテクセルフォーマットを定義するチャンネルフォーマットは、チャンネルフォーマット表に記載されています。 最後から2番目の列は、格納ビットからシェーダ値への変換方法(チャンネル転送関数、CTF)を示します。 最後の列は、シェーダ値から格納ビットへの変換方法(逆チャンネル転送関数、ICTF)を示します。
注: 8unormのチャンネル転送関数は{0,...,255}を浮動小数点区間[0.0, 1.0]に写像します。
注: 8snormのチャンネル転送関数は{-128,...,127}を浮動小数点区間[-1.0, 1.0]に写像します。
チャンネルフォーマット | 格納ビット数 | 格納ビットの解釈 | シェーダ型 | シェーダ値(チャンネル転送関数) | 書き込み値 T (逆チャンネル転送関数)
|
---|---|---|---|---|---|
8unorm | 8 | 符号なし整数 v ∈ {0,...,255} | f32 | v ÷ 255 | max(0, min(1, T ))
|
8snorm | 8 | 符号付き整数 v ∈ {-128,...,127} | f32 | v ÷ 127 | max(-1, min(1, T ))
|
8uint | 8 | 符号なし整数 v ∈ {0,...,255} | u32 | v | min(255, T )
|
8sint | 8 | 符号付き整数 v ∈ {-128,...,127} | i32 | v | max(-128, min(127, T ))
|
16unorm | 16 | 符号なし整数 v ∈ {0,...,65535} | f32 | v ÷ 65535 | max(0, min(1, T ))
|
16snorm | 16 | 符号付き整数 v ∈ {-32768,...,32767} | f32 | v ÷ 32767 | max(-1, min(1, T ))
|
16uint | 16 | 符号なし整数 v ∈ {0,...,65535} | u32 | v | min(65535, T )
|
16sint | 16 | 符号付き整数 v ∈ {-32768,...,32767} | i32 | v | max(-32768, min(32767, T ))
|
16float | 16 | IEEE-754 binary16 16ビット浮動小数点値 v | f32 | v | quantizeToF16(T)
|
32uint | 32 | 32ビット符号なし整数値 v | u32 | v | T
|
32sint | 32 | 32ビット符号付き整数値 v | i32 | v | T
|
32float | 32 | IEEE-754 binary32 32ビット浮動小数点値 v | f32 | v | T
|
2unorm | 2 | 符号なし整数 v ∈ {0,...,3} | f32 | v ÷ 3 | max(0, min(1, T ))
|
2uint | 2 | 符号なし整数 v ∈ {0,...,3} | u32 | v | min(3, T )
|
10unorm | 10 | 符号なし整数 v ∈ {0,...,1023} | f32 | v ÷ 1023 | max(0, min(1, T ))
|
10uint | 10 | 符号なし整数 v ∈ {0,...,1023} | u32 | v | min(1023, T )
|
10float | 10 | 10ビット浮動小数点値:5ビットバイアス指数、5ビット仮数 v | f32 | v | max(0, T )
|
11float | 11 | 11ビット浮動小数点値:5ビットバイアス指数、6ビット仮数 v | f32 | v | max(0, T )
|
以下のストレージテクスチャ用テクセルフォーマット表のテクセルフォーマットは、WebGPU plain color formatsに対応し、WebGPU STORAGE_BINDING
用途で少なくとも1つのアクセスモードをサポートします。
これらのテクセルフォーマットはストレージテクスチャ型(§ 6.5.5
ストレージテクスチャ型)のパラメータとして使われます。
テクセルフォーマットが4チャンネルすべてを持たない場合:
-
テクセルの読み出し時、チャンネル転送関数が成分ごとに適用されます:
-
緑チャンネルがない場合、シェーダ値の第2成分は0。
-
青チャンネルがない場合、シェーダ値の第3成分は0。
-
アルファチャンネルがない場合、シェーダ値の第4成分は1。
-
-
テクセルの書き込み時、逆チャンネル転送関数が成分ごとに適用され、欠落チャンネル用のシェーダ値成分は無視されます。
下表の最後の列は、チャンネルフォーマット表のフォーマット固有チャンネル転送関数を使用しています。
テクセルフォーマット | チャンネルフォーマット | メモリ順チャンネル | 対応するシェーダ値 |
---|---|---|---|
rgba8unorm | 8unorm | r, g, b, a | vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba8snorm | 8snorm | r, g, b, a | vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba8uint | 8uint | r, g, b, a | vec4<u32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba8sint | 8sint | r, g, b, a | vec4<i32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba16unorm | 16unorm | r, g, b, a | vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba16snorm | 16snorm | r, g, b, a | vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba16uint | 16uint | r, g, b, a | vec4<u32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba16sint | 16sint | r, g, b, a | vec4<i32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba16float | 16float | r, g, b, a | vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rg8unorm | 8unorm | r, g | vec4<f32>(CTF(r), CTF(g), 0.0, 1.0) |
rg8snorm | 8snorm | r, g | vec4<f32>(CTF(r), CTF(g), 0.0, 1.0) |
rg8uint | 8uint | r, g | vec4<u32>(CTF(r), CTF(g), 0u, 1u) |
rg8sint | 8sint | r, g | vec4<i32>(CTF(r), CTF(g), 0, 1) |
rg16unorm | 16unorm | r, g | vec4<f32>(CTF(r), CTF(g), 0.0, 1.0) |
rg16snorm | 16snorm | r, g | vec4<f32>(CTF(r), CTF(g), 0.0, 1.0) |
rg16uint | 16uint | r, g | vec4<u32>(CTF(r), CTF(g), 0u, 1u) |
rg16sint | 16sint | r, g | vec4<i32>(CTF(r), CTF(g), 0, 1) |
rg16float | 16float | r, g | vec4<f32>(CTF(r), CTF(g), 0.0, 1.0) |
r32uint | 32uint | r | vec4<u32>(CTF(r), 0u, 0u, 1u) |
r32sint | 32sint | r | vec4<i32>(CTF(r), 0, 0, 1) |
r32float | 32float | r | vec4<f32>(CTF(r), 0.0, 0.0, 1.0) |
rg32uint | 32uint | r, g | vec4<u32>(CTF(r), CTF(g), 0u, 1u) |
rg32sint | 32sint | r, g | vec4<i32>(CTF(r), CTF(g), 0, 1) |
rg32float | 32float | r, g | vec4<f32>(CTF(r), CTF(g), 0.0, 1.0) |
rgba32uint | 32uint | r, g, b, a | vec4<u32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba32sint | 32sint | r, g, b, a | vec4<i32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgba32float | 32float | r, g, b, a | vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
bgra8unorm | 8unorm | b, g, r, a | vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
r8unorm | 8unorm | r | vec4<f32>(CTF(r), 0.0, 0.0, 1.0) |
r8snorm | 8snorm | r | vec4<f32>(CTF(r), 0.0, 0.0, 1.0) |
r8uint | 8uint | r | vec4<u32>(CTF(r), 0u, 0u, 1u) |
r8sint | 8sint | r | vec4<i32>(CTF(r), 0, 0, 1) |
r16unorm | 16unorm | r | vec4<f32>(CTF(r), 0.0, 0.0, 1.0) |
r16snorm | 16snorm | r | vec4<f32>(CTF(r), 0.0, 0.0, 1.0) |
r16uint | 16uint | r | vec4<u32>(CTF(r), 0u, 0u, 1u) |
r16sint | 16sint | r | vec4<i32>(CTF(r), 0, 0, 1) |
r16float | 16float | r | vec4<f32>(CTF(r), 0.0, 0.0, 1.0) |
rgb10a2unorm | r, g, b: 10unorm a: 2unorm | r, g, b, a | vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rgb10a2uint | r, g, b: 10uint a: 2uint | r, g, b, a | vec4<u32>(CTF(r), CTF(g), CTF(b), CTF(a)) |
rg11b10ufloat | r, g: 11float b: 10float | r, g, b | vec4<f32>(CTF(r), CTF(g), CTF(b), 1.0) |
WGSLは、表内の各テクセルフォーマットについて事前宣言列挙値を用意しています。
6.5.2. サンプルテクスチャ型
サンプルテクスチャは、 サンプラーと組み合わせてアクセスできます。 サンプラーを使わずにアクセスすることも可能です。 サンプルテクスチャは読み出しアクセスのみ許可します。
テクセルフォーマットは、テクスチャ変数にバインドされた
GPUTexture
の format
属性です。
WebGPU は、テクスチャ、バインドグループレイアウトの sampleType
、
およびテクスチャ変数の サンプル型 の互換性を
検証します。
テクスチャはサンプル型でパラメータ化され、
必ず
f32
、i32
、u32
のいずれかでなければなりません。
型 | 次元性 | 配列化 |
---|---|---|
texture_1d<T> | 1D
| なし |
texture_2d<T> | 2D
| なし |
texture_2d_array<T> | 2D
| あり |
texture_3d<T> | 3D
| なし |
texture_cube<T> | Cube
| なし |
texture_cube_array<T> | Cube
| あり |
-
Tはサンプル型です。
-
画像のパラメータ化型はサンプリング後の変換型です。 例えば、8bit unorm成分のテクセル画像でも、サンプリングすると32ビット浮動小数点(またはvec-of-f32)になります。
6.5.3. マルチサンプルテクスチャ型
マルチサンプルテクスチャは サンプル数が1以上です。 名前に反して、サンプラーと組み合わせて使うことはできません。 実質的に、サンプルインデックスを無視すれば各論理テクセルアドレスごとに複数のテクセル分のデータを保持します。
テクセルフォーマットは、テクスチャ変数にバインドされた
GPUTexture
の format
属性です。
WebGPU は、テクスチャ、バインドグループレイアウトの sampleType
、
およびテクスチャ変数の サンプル型 の互換性を
検証します。
texture_multisampled_2d
はサンプル型でパラメータ化され、
必ず
f32
、i32
、u32
のいずれかでなければなりません。
型 | 次元性 | 配列化 |
---|---|---|
texture_multisampled_2d<T> | 2D
| なし |
texture_depth_multisampled_2d | 2D
| なし |
-
Tはサンプル型です。
6.5.4. 外部サンプルテクスチャ型
外部テクスチャは不透明な2次元float-サンプルテクスチャ型であり、texture_2d<f32>
に似ていますが、異なる表現を持つ可能性があります。
textureLoadや
textureSampleBaseClampToEdgeなどの組み込み関数で読み出しでき、これらは異なる表現を扱います。
WebGPU § 6.4 GPUExternalTexture参照。
型 | 次元性 | 配列化 |
---|---|---|
texture_external | 2D
| なし |
6.5.5. ストレージテクスチャ型
ストレージテクスチャは、サンプラーを使わず個別テクセル値へのアクセスをサポートします。
-
書き込み専用ストレージテクスチャ は、個別テクセルへの書き込みをサポートし、シェーダ値から格納テクセル値への自動変換を行います。
-
読み出し専用ストレージテクスチャ は、個別テクセルの読み出しをサポートし、格納テクセル値からシェーダテクセル値への自動変換を行います。
-
読書き両用ストレージテクスチャ は、個別テクセルの読み出し・書き込みをサポートし、シェーダ値・格納値間の自動変換を行います。
ストレージテクスチャ型は、ストレージテクスチャ用テクセルフォーマットのいずれかでパラメータ化されなければなりません。 テクセルフォーマットにより§ 6.5.1 テクセルフォーマットで定義された変換関数が決まります。
ストレージテクスチャへテクセルを書き込む際は、変換関数の逆が用いられ、シェーダ値が格納テクセル値に変換されます。
型 | 次元性 | 配列化 |
---|---|---|
texture_storage_1d<Format, Access> | 1D
| なし |
texture_storage_2d<Format, Access> | 2D
| なし |
texture_storage_2d_array<Format, Access> | 2D
| あり |
texture_storage_3d<Format, Access> | 3D
| なし |
-
Formatはストレージテクスチャ用テクセルフォーマットの列挙値でなければなりません
-
FormatとAccessの組み合わせが無効でもシェーダ生成エラーにはなりません。 組み合わせの検証はパイプライン生成時のシェーダバインド検証段階で行われ、 無効な組み合わせはパイプライン生成エラーとなります。
6.5.6. 深度テクスチャ型
深度テクスチャは、 sampler_comparisonと組み合わせてアクセス可能です。 サンプラーを使わずにアクセスすることもできます。 深度テクスチャは読み出しアクセスのみ許可されます。
テクスチャのテクセルフォーマットはGPUTextureBindingLayout
で定義されます。
型 | 次元性 | 配列化 |
---|---|---|
texture_depth_2d | 2D
| なし |
texture_depth_2d_array | 2D
| あり |
texture_depth_cube | Cube
| なし |
texture_depth_cube_array | Cube
| あり |
6.5.7. サンプラー型
サンプラーは、サンプルテクスチャ や深度テクスチャへのアクセスを調整し、以下の操作を組み合わせて行います:
サンプラー型は以下の通りです:
型 | 説明 |
---|---|
sampler | サンプラー。サンプルテクスチャへのアクセスを調整します。 |
sampler_comparison | 比較サンプラー。深度テクスチャへのアクセスを調整します。 |
サンプラーはWebGPU APIで生成時にパラメータ化され、WGSLモジュールでは変更できません。
サンプラーはテクスチャ組み込み関数でのみ使用できます。
sampler sampler_comparison
6.6. AllTypes型
AllTypes型は、WGSLのすべての型の集合です。
AllTypes型はWGSLソースで記述する方法はありません。
すべての§ 6.9 事前宣言型・型ジェネレータ一覧を参照してください。
AllTypes型が存在する理由は、型チェック規則が、通常の値を含み得る句に適用されるようにするためです。 WGSLでは型も値の一種と定義し、式が型を表せるようにすることで、規則を一貫性のあるものにしています。
主な例はテンプレートパラメータであり、様々な文脈で型、列挙値、プレーン値など複数のものを表します。 特にtemplate_arg_expression文法規則はexpression文法非終端記号に展開されます。
6.7. 型エイリアス
型エイリアスは既存の型に新しい名前を付けて宣言するものです。 宣言は必ずモジュールスコープで行い、そのスコープはプログラム全体です。
型Tが構造体型Sの型エイリアスとして定義された場合、 Sのメンバーのすべての属性を含むプロパティはTにも継承されます。
注: エイリアスされる型が value constructor の組み込み関数をサポートしている場合、 型エイリアスが スコープ内 であり、関数呼び出しが他の点で有効な場合は、 その関数を元の型名ではなくエイリアス名から呼び出すことができます。
'alias'
ident '='
type_specifier
alias Arr = array< i32, 5 > ; alias RTArr = array< vec4< f32>> ; alias single = f32; // f32のエイリアス宣言 const pi_approx : single = 3.1415 ; fn two_pi () -> single { return single ( 2 ) * pi_approx ; }
6.8. 型指定子文法
§ 8.17 型式参照。
注:式も型を表すことができます。これはprimary_expression文法規則からtemplate_elaborated_identに展開され、括弧付き式経由でも表せます。
6.9. 事前宣言型・型ジェネレータ一覧
WGSLはfrexp、 modf、atomicCompareExchangeWeak組み込み関数の戻り値型も事前宣言しますが、WGSLソースで記述することはできません。
事前宣言型ジェネレータは下表の通りです:
事前宣言型ジェネレータ | 参照先 |
---|---|
array | § 6.2.9 配列型参照 |
atomic | § 6.2.8 アトミック型参照 |
mat2x2 |
§ 6.2.7 行列型参照(行列型の型エイリアスも一覧)
注:これらは値コンストラクタ式でも行列生成に使われます。 |
mat2x3 | |
mat2x4 | |
mat3x2 | |
mat3x3 | |
mat3x4 | |
mat4x2 | |
mat4x3 | |
mat4x4 | |
ptr | § 6.4.3 参照型・ポインター型参照 |
texture_1d | § 6.5.2 サンプルテクスチャ型参照 |
texture_2d | |
texture_2d_array | |
texture_3d | |
texture_cube | |
texture_cube_array | |
texture_multisampled_2d | § 6.5.3 マルチサンプルテクスチャ型参照 |
texture_storage_1d | § 6.5.5 ストレージテクスチャ型参照 |
texture_storage_2d | |
texture_storage_2d_array | |
texture_storage_3d | |
vec2 |
§ 6.2.6 ベクトル型参照(ベクトル型の型エイリアスも一覧)
注:これらは値コンストラクタ式でもベクトル生成に使われます。 |
vec3 | |
vec4 |
7. 変数・値の宣言
値宣言は値に名前を付け、その値は宣言後不変です。
値宣言にはconst
、override
、let
、仮引数宣言の4種があり、詳細は下記(§ 7.2 値の宣言)参照。
変数宣言は、値を格納するメモリ位置に名前を付けます。
ここに格納された値は、変数がread_writeアクセスモードの場合、更新可能です。
変数宣言はvar
の1種ですが、アドレス空間やアクセスモードの組み合わせ指定が可能です。詳細は下記(§ 7.3 var宣言)参照。
注: 値宣言には対応するメモリ位置はありません。例えば、WGSL式で値へのポインターを作ることはできません。
関数定義外で宣言されたものはモジュールスコープとなり、その名前はプログラム全体でスコープ内です。
関数定義内で宣言されたものは関数スコープです。 名前は宣言直後の文から、宣言を囲む波括弧付き文リストの末尾まで有効です。 関数スコープ宣言は動的コンテキストです。
[...]
は省略可能、...*
は0回以上の繰り返し、...+
は1回以上の繰り返しを示します。詳細な構文規則は各要素の該当節を参照してください。
// 特定の値宣言 const name [: type ] = initializer ; [ attribute ] * override name [: type ] [ = initializer ]; let name [: type ] = initializer ; // 一般的な変数宣言 [ attribute ] * var [ < address_space [, access_mode ] > ] name [: type ] [ = initializer ]; // 特定の変数宣言 // 関数スコープ var [ < function> ] name [: type ] [ = initializer ]; // モジュールスコープ var < private> name [: type ] [ = initializer ]; var < workgroup> name : type ; [ attribute ] + var < uniform> name : type ; [ attribute ] + var name : texture_type ; [ attribute ] + var name : sampler_type ; [ attribute ] + var < storage[, access_mode ] > name : type ;
これらの宣言は必ず型または初期化子を明示指定しなければなりません。 型・初期化子の両方指定も可能です。 これらの宣言は関連データ値の型、すなわち宣言の有効値型を決定します。 有効値型は以下の通りです:
-
型を明示指定した場合はその型。
-
そうでない場合、初期化子式の型が
T
なら:-
const
宣言の場合は有効値型はT
。 -
override
、let
、var
宣言の場合、有効値型はT
の具象化。
-
値宣言・変数宣言の各種は、初期化子式や有効値型に追加の制約を課す場合があります。
宣言 | 可変性 | スコープ | 有効値型1 | 初期化子サポート | 初期化子式2 | リソースインターフェースの一部 |
---|---|---|---|---|---|---|
const | 不変 | モジュールまたは関数 | 構築可能型(具象型または抽象型) | 必須 | 定数式 | いいえ |
override | 不変 | モジュール | 具象スカラー | 任意3 | 定数式またはoverride式 | いいえ4 |
let | 不変 | 関数 | 具象 構築可能型または ポインター型 | 必須 | 定数式、override式、実行時式 | いいえ |
var<storage, read> var<storage> | 不変 | モジュール | 具象 ホスト共有型 | 不可 | はい。 ストレージバッファ | |
var<storage, read_write>5,6 | 可変 | モジュール | 具象 ホスト共有型 | 不可 | はい。 ストレージバッファ | |
var<uniform> | 不変 | モジュール | 具象 構築可能型 ホスト共有型 | 不可 | はい。 ユニフォームバッファ | |
var6 | 不変7 | モジュール | テクスチャ型 | 不可 | はい。 テクスチャリソース | |
var | 不変 | モジュール | サンプラー型 | 不可 | はい。 サンプラーリソース | |
var<workgroup>6,8 | 可変 | モジュール | 具象 プレーン型で固定フットプリントあり9 | 不可10 | いいえ | |
var<private> | 可変 | モジュール | 具象 構築可能型 | 任意10 | 定数式またはoverride式 | いいえ |
var<function> var | 可変 | 関数 | 具象 構築可能型 | 任意10 | 定数式、override式、実行時式 | いいえ |
-
初期化子未指定の場合、パイプライン生成時に値を与える必要あり。
-
override宣言はシェーダインターフェースの一部だが、バインドリソースではない。
-
ストレージバッファやストレージテクスチャは、read以外のアクセスモードでは静的アクセスできません。WebGPU
createBindGroupLayout()
参照。 -
アトミック型は可変ストレージバッファかworkgroup変数のみ可。
-
ストレージテクスチャはwriteやread_writeアクセスモードでデータが可変だが、変更はtextureStore組み込み関数のみ可能。変数自体は変更不可。
-
workgroupアドレス空間の変数はコンピュートシェーダステージでのみ静的アクセス可能。
-
初期化子がない場合、変数はデフォルト初期化される。
7.1. 変数と値の違い
変数宣言はWGSLモジュールで唯一可変なデータです。 値宣言は常に不変です。 変数は参照型やポインター型値の基礎となりますが、値宣言はポインターや参照型値の基礎にはなりません。
変数の利用は値宣言より一般にコストが高く、変数利用は変数に紐付くメモリ位置への読み出しや書き込み操作が追加されるためです。
一般に、次の順で宣言を使うのが推奨されます(上ほど推奨):
この順に使うことで、シェーダ全体の性能が最も高くなります。
7.2. 値の宣言
識別子が解決されて値宣言を指す場合、その識別子はその値を表します。
WGSLは複数種の値宣言を提供します。 それぞれの宣言種の値は、シェーダライフサイクルの異なるタイミングで確定します。 宣言種ごとに値が確定するタイミングは以下の通りです:
7.2.1. const
宣言
const宣言は、シェーダ生成時に確定するデータ値に名前を与えます。 const宣言は必ず初期化子が必要です。 モジュールスコープまたは関数スコープで宣言できます。 初期化子式は必ず 定数式でなければなりません。 const宣言の型は具象型または抽象型の構築可能型でなければなりません。 const宣言だけが有効値型として抽象型を持てます。
注: 抽象数値型はWGSLで記述できないため、型推論経由のみ使用可能です。
const a = 4 ; // 値が 4 の AbstractInt。 const b : i32= 4 ; // 値が 4 の i32。 const c : u32= 4 ; // 値が 4 の u32。 const d : f32= 4 ; // 値が 4 の f32。 const e = vec3( a , a , a ); // 値が (4, 4, 4) の AbstractInt の vec3。 const f = 2.0 ; // 値が 2 の AbstractFloat。 const g = mat2x2( a , f , a , f ); // AbstractFloat の mat2x2 の値: // ((4.0, 2.0), (4.0, 2.0))。 // AbstractInt a は AbstractFloat に変換される。 // AbstractFloat は AbstractInt に変換できない。 const h = array( a , f , a , f ); // 4 要素の AbstractFloat の array: // (4.0, 2.0, 4.0, 2.0)。
7.2.2. override
宣言
override宣言は、パイプライン上書き可能な定数値に名前を付けます。 override宣言は必ずモジュールスコープで宣言します。 パイプライン上書き可能定数値はパイプライン生成時に確定します。 WebGPUパイプライン生成メソッドで指定された値があればそれが使われ、なければ具象化された初期化子式の値が使われます。 override宣言の有効値型は具象スカラー型でなければなりません。
初期化子式は省略可能です。 ある場合はoverride式でなければならず、これはパイプライン上書き可能定数のデフォルト値を表します。 初期化子がない場合、パイプライン生成時に値が与えられないとパイプライン生成エラーとなります。
宣言にid属性が付与されている場合、そのリテラルはパイプライン定数IDと呼ばれ、0〜65535の一意な整数でなければなりません。 2つのoverride宣言が同じパイプライン定数IDを使うことはできません。
アプリケーションは、パイプライン生成時にoverride宣言に対して独自の値を指定できます。 パイプライン生成APIは、上書き可能な定数からその定数型の値へのマッピングを受け付けます。 定数はパイプライン上書き可能定数識別子文字列によって識別されます。これは、パイプライン定数IDが指定されていればその10進表現、指定されていなければ宣言された名前です。
@id ( 0 ) override has_point_light : bool= true ; // アルゴリズム制御 @id ( 1200 ) override specular_param : f32= 2.3 ; // 数値制御 @id ( 1300 ) override gain : f32; // 必ず上書きされる必要あり override width : f32= 0.0 ; // APIレベルで"name"で指定 override depth : f32; // APIレベルで"depth"で指定。必ず上書き必要 override height = 2 * depth ; // デフォルト値(APIで未指定の場合)、他の上書き可能定数に依存
7.2.3. let
宣言
let宣言は、実行時に文が実行されるたびに値が確定する名前を定義します。 let宣言は関数スコープのみで宣言可能であり、動的コンテキストです。 let宣言は必ず初期化子式が必要です。 値は初期化子の具象化された値です。 let宣言の有効値型は具象構築可能型またはポインター型でなければなりません。
// 'blockSize'はi32値1024を表す let blockSize : i32= 1024 ; // 'row_size'はu32値16uを表す。型は推論される。 let row_size = 16u ;
7.3. var
宣言
変数は、特定の格納可能型の値を保持できるメモリへの名前付き参照です。
変数には2つの型が関連付けられます:格納型(参照メモリに格納可能な値の型)と参照型(変数自体の型)。
変数の格納型がT
、アドレス空間がAS
、アクセスモードがAM
なら、参照型はref<AS,T,AM>
となります。
変数の格納型は常に具象型です。
変数宣言は以下を行います:
-
変数の名前を指定する。
-
変数のアドレス空間、格納型、アクセスモードを決定する。 これらが変数の参照型を構成します。
-
格納型は変数宣言の有効値型です。
-
-
実行環境が、指定アドレス空間・指定アクセスモードで格納型値のメモリを、変数の有効期間中割り当てることを保証します。
-
変数がprivateまたはfunctionアドレス空間の場合は初期化子式を持てます。 初期化子がある場合、変数の格納型に評価されなければなりません。 private変数の初期化子は定数式またはoverride式でなければなりません。 function・private以外のアドレス空間の変数は初期化子指定不可です。
識別子が変数宣言を解決する場合、その識別子は変数のメモリへの参照メモリビューを表す式となり、その型は変数の参照型です。§ 8.11 変数識別子式参照。
変数宣言のアドレス空間やアクセスモードをWGSLソースで指定する場合は、var
キーワード後にテンプレートリストで記述します:
private、storage、uniform、workgroup、handleはモジュールスコープのみ、 functionは関数スコープのみ宣言可。 handleとfunction以外のアドレス空間は必ず指定しなければならず、handleは指定不可。 functionアドレス空間指定はオプション。
アクセスモードは必ずデフォルト値を持ち、storage以外ではWGSLソースに指定不可です。§ 14.3 アドレス空間参照。
uniformアドレス空間の変数はユニフォームバッファ変数です。 格納型はホスト共有かつ構築可能型で、アドレス空間レイアウト制約を満たす必要があります。
storageアドレス空間の変数はストレージバッファ変数です。 格納型はホスト共有型で、アドレス空間レイアウト制約を満たす必要があります。 readまたはread_writeアクセスモードで宣言可能で、デフォルトはreadです。
テクスチャリソースは、有効値型がテクスチャ型の変数です。 モジュールスコープで宣言され、基底のテクセルグリッドへアクセスするための不透明ハンドルを持ちます。 ハンドル自体はhandleアドレス空間にあり、常にread-onlyです。 多くの場合、基底テクセルもread-onlyで、テクスチャ変数は不変とされます。 書き込み専用ストレージテクスチャや読書き両用ストレージテクスチャでは基底テクセルが可変なので、テクスチャ変数は慣習的に可変とされます。
サンプラーリソースは、有効値型がサンプラー型の変数です。 モジュールスコープで宣言され、handleアドレス空間に存在し、不変です。
§ 13.3.2 リソースインターフェースで述べたように、ユニフォームバッファ・ストレージバッファ・テクスチャ・サンプラーはシェーダのリソースインターフェースを構成します。
変数の有効期間は、シェーダ実行中その変数に割り当てられたメモリ位置が維持される期間です。 モジュールスコープ変数の有効期間はシェーダステージの全実行期間です。 privateとfunctionアドレス空間の変数は呼び出しごとに独立したバージョンを持ちます。 関数スコープ変数は動的コンテキストです。 関数スコープ変数の有効期間はスコープによって決まります:
2つのリソース変数が重複メモリ位置を持つ場合、どちらかが可変なら動的エラーです。 その他の変数は有効期間が重なっても重複メモリ位置は持ちません。 変数の有効期間が終わると、そのメモリは他の変数用に使われることがあります。
注: WGSLは変数の内容が有効期間中のみ可視となることを保証します。
private、function、 workgroupアドレス空間の変数は生成時に初期値を持ちます。 初期化子がなければ初期値はデフォルト初期値です。 初期値は以下のように決定されます:
-
functionアドレス空間の場合:
-
privateアドレス空間の場合:
-
workgroupアドレス空間の場合:
他のアドレス空間の変数は、描画コマンドやディスパッチコマンドでバインドされたリソースによって設定されます。
以下はWGSLコード例です:
var i : i32; // 初期値は0。推奨されないスタイル。 loop { var twice : i32= 2 * i ; // 各イテレーションごとに再評価 i ++ ; if i == 5 { break ; } }
i
は0, 1, 2, 3, 4, 5の値を、変数twice
は0, 2, 4, 6, 8の値を取ります。
以下はWGSLコード例です:
x
は変数なので、すべてのアクセスはロード/ストア操作となります。
ただし、ブラウザやドライバが中間表現最適化により冗長なロードを除去することが期待されます。
var < private> decibels : f32; var < workgroup> worklist : array< i32, 10 > ; struct Params { specular : f32, count : i32} // ユニフォームバッファ。常にread-onlyで、レイアウト制約が厳しい。 @group ( 0 ) @binding ( 2 ) var < uniform> param : Params ; // ユニフォームバッファ // 読み書き可能なストレージバッファ @group ( 0 ) @binding ( 0 ) var < storage, read_write> pbuf : array< vec2< f32>> ; // テクスチャとサンプラーは常に"handle"空間 @group ( 0 ) @binding ( 1 ) var filter_params : sampler;
// ストレージバッファ @group ( 0 ) @binding ( 0 ) var < storage, read> buf1 : Buffer ; // 読み出しのみ、書き込み不可 @group ( 0 ) @binding ( 0 ) var < storage> buf2 : Buffer ; // 読み出しのみ、書き込み不可 @group ( 0 ) @binding ( 1 ) var < storage, read_write> buf3 : Buffer ; // 読み書き可能 struct ParamsTable { weight : f32} // ユニフォームバッファ。常にread-onlyで、レイアウト制約が厳しい。 @group ( 0 ) @binding ( 2 ) var < uniform> params : ParamsTable ; // 読み出しのみ、書き込み不可
fn f () { var < function> count : u32; // functionアドレス空間の変数 var delta : i32; // もう一つのfunctionアドレス空間変数 var sum : f32= 0.0 ; // 初期化子付きfunctionアドレス空間変数 var pi = 3.14159 ; // 初期化子からf32格納型を推論 }
7.4. 変数・値宣言の文法まとめ
| variable_decl '='
expression
| 'let'
optionally_typed_ident '='
expression
| 'const'
optionally_typed_ident '='
expression
'var'
_disambiguate_template template_list ? optionally_typed_ident
ident ( ':'
type_specifier ) ?
attribute * variable_decl ( '='
expression ) ?
'const'
optionally_typed_ident '='
expression
| attribute * 'override'
optionally_typed_ident ( '='
expression ) ?
8. 式
式は値の計算方法を指定します。
値式の種類ごとに、評価タイミングと表現力とのトレードオフがあります。 早い評価ほど操作が制約されますが、値の利用できる場所が増えます。このトレードオフにより、値宣言ごとに異なる柔軟性が生じます。 定数式とoverride式は、GPUの実行前に評価されるため、最終GPUコードには式の計算結果のみが必要です。 また、定数式はシェーダ生成時に評価されるため、override式より多くの場面で利用できます(例:関数スコープ変数の配列サイズ指定など)。 実行時式は、定数式でもoverride式でもない式です。 実行時式はシェーダ実行中GPU上で計算されます。 実行時式は利用できる文法要素が少ないですが、他の実行時値などより広い式から計算可能です。
8.1. 早期評価式
WGSLは実行時前に評価可能な2種類の式を定義します:
8.1.1. const
式
シェーダ生成時に評価できる式をconst式と呼びます。 式がconst式となるのは、そのすべての識別子が以下のいずれかに解決される場合です:
const
式の型は必ず型規則により生成時固定フットプリント型に決定されなければなりません。
注: 抽象型はconst式の型推論で用いられることがあります。
const式Eは、以下の場合のみ評価されます:
注:
この評価規則により、短絡評価演算子&&
や||
は右辺部分式の評価をガードします。ただし、部分式が静的型決定のために評価を要する場合は除きます。
const式はWebGPU APIメソッドを実装するCPUによって評価される場合があります。 したがって、AbstractFloat値の演算の精度要件は、WebGPUの一般的な実行環境(WebAssembly [WASM-CORE-2]やECMAScript [ECMASCRIPT]など)で要求される以上に厳しくありません。 具体的な浮動小数点型(f32など)の精度要件は§ 15.7.4.1 具体的浮動小数点式の精度で規定されています。
例:(42)
の解析:
-
42
はAbstractInt値42です。 -
それを括弧で囲むことで、型がAbstractIntで値42の新しい式
(42)
となります。
例:-5
の解析:
-
5
はAbstractInt値5です。 -
その前に '
-
' を付すことで、型がAbstractIntで値-5の新しい式-5
となります。
例:-2147483648
の解析:
-
2147483648
はAbstractInt値2147483648です。 この値は32ビット符号付き整数には収まりません。 -
その前に '
-
' を付すことで、型がAbstractIntで値-2147483648の新しい式-2147483648
となります。
例:const minint = -2147483648;
の解析:
-
上述の通り、
-2147483648
はAbstractInt値-2147483648です。 -
結果として、
minint
はAbstractInt値-2147483648として宣言されます。
例:let minint = -2147483648;
の解析:
-
上述の通り、
-2147483648
はAbstractInt値-2147483648です。 -
明示型がないため、オーバーロード解決が行われます。 適用候補は自動変換可能な型へのものです。AbstractIntからはi32、u32、f32への候補があります。 最優先はi32への変換なので、AbstractInt値-2147483648はi32値-2147483648に変換されます。
-
結果として、
minint
はi32値-2147483648として宣言されます。
例:false && (10i < i32(5 * 1000 * 1000 * 1000))
の解析:
-
式全体がconst式です。
-
ただし、
&&
演算子の短絡評価規則により、左辺はfalse
となるため右辺は評価されません。 -
i32(5 * 1000 * 1000 * 1000) の評価は、シェーダ生成エラーとなります。なぜなら、AbstractInt値5000000000はi32型でオーバーフローするためです。
例:false && array<u32, 1 + 2>(0, 1, 2)[0] == 0
-
式全体がconst式です。
-
型チェックでは
e1 : bool && e2 : bool
が要求されます:-
false
はbool値です。 -
右辺の型チェックも進み、配列要素数式の
1 + 2
が評価されます。
-
-
1 + 2
はi32値3
に評価されます。-
配列型は
array<u32, 3i>
となります。
-
-
配列アクセス式や等価演算子は評価されません。
8.1.2. override
式
パイプライン生成時に評価できる式はoverride式と呼びます。 式がoverride式となるのは、そのすべての識別子が以下のいずれかに解決される場合です:
注: すべてのconst式はoverride式でもあります。
const式以外のoverride式は、パイプライン生成時にのみ検証・評価され、APIで提供された値がoverride宣言に代入された後のみです。 override宣言がAPI経由で値を代入された場合、初期化子式があっても評価されません。 それ以外の場合、override式Eは以下の場合のみ評価されます:
-
Eが
GPUProgrammableStage
で指定されたエントリポイント用シェーダの一部であり: -
次のいずれかが真である:
-
Eが値代入後のトップレベル式である場合
-
Eが式OuterEの部分式で、OuterE が評価され、かつOuterEの評価にEの評価が必要な場合
-
Eが式OuterEの部分式で、OuterEがパイプライン生成エラーを生じるためにEの評価が必要な場合 (例:整数除算)
-
注: すべてのoverride式がoverride宣言の初期化子として使えるわけではありません。なぜなら、初期化子は型規則で具象スカラー型に決定されなければならないためです。
例:override x = 42;
の解析:
-
42
はAbstractInt値42です。 -
override宣言は具象スカラー型が必要です。
例:let y = x + 1;
の解析:
-
上記より、
x
はi32型です。 -
x + 1
はoverride宣言と整数リテラルから構成されているためoverride式です。 -
この式はi32型であり、パイプライン生成時に評価されます。 値は
x
がパイプライン生成時に上書きされるかどうかに依存します。
例:vec3(x,x,x)
の解析:
-
上記より、
x
はoverride宣言で型はi32です。 -
vec3(x,x,x)
は識別子がすべてoverride宣言に解決されるためoverride式です。
override a : i32= 0 ; override b = a / 0 ; // シェーダ生成エラー // cをoverrideしようとしても関係なく発生
override a : i32= 0 ; override b = 1 / a ; // bはfrag1シェーダの一部。frag1をパイプラインにコンパイルする場合: // * bがoverrideされていればエラーなし // * aが非ゼロ値にoverrideされていればエラーなし // * aが0かつbがoverrideされていなければパイプライン生成エラー @fragment fn frag1 () { _ = b ; } // bはfrag2シェーダには含まれない。frag2をパイプラインにコンパイルする場合: // bがoverrideされていなくてもaが0でもエラーにならない。 @fragment fn frag2 () { }
8.2. 不定値
限定された場合に、実行時式の評価が、その部分式に対して非対応値を使ったときに生じ得ます。
この場合、評価結果は式の静的型の不定値となり、 これは静的型における実装依存の任意値を意味します。
評価が繰り返しごとなど複数の動的コンテキストで生じる場合、個々に異なる値が生成されることがあります。 例:ループ各イテレーションごとに異なる値になる場合があります。
注: 型が浮動小数点型かつ実装がNaN値をサポートする場合、実行時の不定値はNaNとなる可能性があります。
fn fun () { var extracted_values : array< i32, 2 > ; const v = vec2< i32> ( 0 , 1 ); for ( var i : i32= 0 ; i < 2 ; i ++ ) { // 実行時式によってベクトルをインデックスし、ベクトルのインデックス範囲外の場合、 // ベクトル成分型の不定値が生成される。 let extract = v [ i + 5 ]; // この時点で 'extract' は i32 型の任意の値。 // 後で使うため保存。 extracted_values [ i ] = extract ; if extract == extract { // これは常に実行される } if extract < 2 { // 実行される場合もあるが、されない場合もある。 // 元のベクトル成分は 0 と 1 だが、 // 抽出された値がそれらとは限らない。 } } if extracted_values [ 0 ] == extracted_values [ 1 ] { // 実行される場合もあるが、されない場合もある。 } } fn float_fun ( runtime_index : u32) { const v = vec2< f32> ( 0 , 1 ); // 浮動小数点値のベクトル // 前の例と同様に、'float_extract' は不定値となる。 // 浮動小数点型なので、NaN になる場合がある。 let float_extract : f32= v [ runtime_index + 5 ]; if float_extract == float_extract { // これは *実行されない場合がある*、理由: // - 'float_extract' が NaN の場合があり、 // - NaN は他のどの浮動小数点数とも等しくない(他の NaN でさえも)。 } }
8.3. リテラル値式
前提条件 | 結論 | 説明 |
---|---|---|
true : bool
| true の論理値。
| |
false : bool
| false の論理値。
| |
eがサフィックスなし整数リテラルの場合 | e: AbstractInt | 抽象整数リテラル値。 |
eがサフィックスなし浮動小数点リテラルの場合 | e: AbstractFloat | 抽象浮動小数点リテラル値。 |
eがi サフィックス付き整数リテラルの場合
| e: i32 | 32ビット符号付き整数リテラル値。 |
eがu サフィックス付き整数リテラルの場合
| e: u32 | 32ビット符号なし整数リテラル値。 |
eがf サフィックス付き浮動小数点リテラルの場合
| e: f32 | 32ビット浮動小数点リテラル値。 |
eがh サフィックス付き浮動小数点リテラルの場合
| e: f16 | 16ビット浮動小数点リテラル値。 |
8.4. 括弧付き式
前提条件 | 結論 | 説明 |
---|---|---|
e : T | ( e ) : T
| eとして評価される。 式を周囲のテキストから分離するために括弧を使う。 |
8.5. 合成値分解式
この節では、構成要素を持つ合成値から構成要素を取得する式、 および、合成値へのメモリビューから構成要素への参照を取得する式について説明します。 ここでは、合成値またはそのメモリビューを基底と呼びます。
方法は2つあります:
- 名前付き要素式
-
基底 Bの式の後にピリオド
'.'
(U+002D)と要素名を続ける。 - インデックス式
-
基底式の後に
'['
(U+005B)、インデックス式、']'
(U+005D)を続ける。
これら2つの構文は、component_or_swizzle_specifier文法規則で表されます。
-
基底が行列型、または行列へのメモリビューの場合、Nは行列型の列数です。
インデックス値が有効範囲インデックスでない場合、範囲外インデックスです。 範囲外インデックスは多くの場合プログラムの欠陥であり、多くの場合エラーとなります。 詳細は下記参照。
さらに、ベクトル型は別のベクトルの構成要素から新しいベクトル値を作るスウィズル構文もサポートします。
8.5.1. ベクトル要素アクセス式
ベクトルの構成要素へは:
-
配列添字(例:
v[2]
)でアクセス -
または、スウィズル名(コンテキスト依存名)を使い、便宜的な各名称で元ベクトルの構成要素を指定してアクセス
-
色ベース:
r
,g
,b
,a
はベクトル要素0, 1, 2, 3に対応。 -
次元ベース:
x
,y
,z
,w
はベクトル要素0, 1, 2, 3に対応。
-
便宜名称は.
記法でアクセス(例:color.bgra
)。
便宜レタリングは混在不可(例:.rybw
は不可)。
便宜レタはベクトル末尾要素を超えてアクセス不可。
便宜レタの順序や重複は自由。指定レタ数は1〜4必須。つまり、便宜レタ利用で得られる型はスカラー型または有効なベクトル型のみ。
指定レタ数に応じた型(例:vec4<f32>
の場合)
アクセサ | 結果型 |
---|---|
r | f32
|
rg | vec2<f32>
|
rgb | vec3<f32>
|
rgba | vec4<f32>
|
var a : vec3< f32> = vec3< f32> ( 1. , 2. , 3. ); var b : f32= a . y ; // b = 2.0 var c : vec2< f32> = a . bb ; // c = (3.0, 3.0) var d : vec3< f32> = a . zyx ; // d = (3.0, 2.0, 1.0) var e : f32= a [ 1 ]; // e = 2.0
8.5.1.1. ベクトル単一要素選択
前提条件 | 結論 | 説明 |
---|---|---|
e: vecN<T> |
e.x : Te .r : T
|
eの第1要素を選択
これは1文字のスウィズル。 |
e: vecN<T> |
e.y : Te .g : T
|
eの第2要素を選択
これは1文字のスウィズル。 |
e: vecN<T> Nは3または4 |
e.z : Te .b : T
|
eの第3要素を選択
これは1文字のスウィズル。 |
e: vec4<T> |
e.w : Te .a : T
|
eの第4要素を選択
これは1文字のスウィズル。 |
e: vecN<T> i: i32 または u32 Tは具象型 | e[i]: T |
ベクトルのi番目要素を選択 最初の要素はi=0。 iが範囲[0,N-1]外の場合:
|
e: vecN<T> i: i32 または u32 Tは抽象型 iはconst式 | e[i]: T |
ベクトルのi番目要素を選択 最初の要素はi=0。 iが範囲[0,N-1]外ならシェーダ生成エラー。 注: 抽象ベクトル値eをconst式でない式でインデックスすると、インデックス適用前に具象化されます。 |
8.5.1.2. ベクトル複数要素選択
この節の式はすべて複数文字のスウィズルです。 それぞれが他のベクトルの構成要素からベクトルを生成します。
複数文字のスウィズルは代入の左辺には使えません: 代入の左辺は参照型でなければなりませんが、複数文字スウィズル式は常にベクトル型の値を生成します。
前提条件 | 結論 | 説明 |
---|---|---|
e: vecN<T> または
ptr<AS,vecN<T,AM>> Iは x , y , z , w のいずれかJは x , y , z , w のいずれかAMは read またはread_write |
e. IJ: vec2<T> | 第1要素e.I、第2要素e.Jの2要素ベクトルを生成。z はNが3または4の時のみ有効。w はNが4の時のみ有効。eがポインターの場合、間接参照後にロード規則が適用される。 |
e: vecN<T> または
ptr<AS,vecN<T,AM>> Iは r , g , b , a のいずれかJは r , g , b , a のいずれかAMは read またはread_write |
e. IJ: vec2<T> | 第1要素e.I、第2要素e.Jの2要素ベクトルを生成。b はNが3または4の時のみ有効。a はNが4の時のみ有効。eがポインターの場合、間接参照後にロード規則が適用される。 |
e: vecN<T> または
ptr<AS,vecN<T,AM>> I, J, Kは x , y , z ,
w のいずれかAMは read またはread_write |
e. IJK: vec3<T> | 第1要素e.I、第2要素e.J、第3要素e.Kの3要素ベクトルを生成。z はNが3または4の時のみ有効。w はNが4の時のみ有効。eがポインターの場合、間接参照後にロード規則が適用される。 |
e: vecN<T> または
ptr<AS,vecN<T,AM>> I, J, Kは r , g , b ,
a のいずれかAMは read またはread_write |
e. IJK: vec3<T> | 第1要素e.I、第2要素e.J、第3要素e.Kの3要素ベクトルを生成。b はNが3または4の時のみ有効。a はNが4の時のみ有効。eがポインターの場合、間接参照後にロード規則が適用される。 |
e: vecN<T> または
ptr<AS,vecN<T,AM>> I, J, K, Lは x , y ,
z , w のいずれかAMは read またはread_write |
e. IJKL:
vec4<T> | 第1要素e.I、第2要素e.J、第3要素e.K、第4要素e.Lの4要素ベクトルを生成。z はNが3または4の時のみ有効。w はNが4の時のみ有効。eがポインターの場合、間接参照後にロード規則が適用される。 |
e: vecN<T> または
ptr<AS,vecN<T,AM>> I, J, K, Lは r , g ,
b , a のいずれかAMは read またはread_write |
e. IJKL:
vec4<T> | 第1要素e.I、第2要素e.J、第3要素e.K、第4要素e.Lの4要素ベクトルを生成。b はNが3または4の時のみ有効。a はNが4の時のみ有効。eがポインターの場合、間接参照後にロード規則が適用される。 |
8.5.1.3. ベクトルメモリビューからの要素参照
この節の式は、ベクトル全体のメモリビューからベクトルの単一要素のメモリビューを生成します。
WGSLの型規則により、これらの式は以下の場合に利用できます:
ベクトル要素への書き込みアクセスは、ベクトルに関連付けられたメモリ位置全体をアクセスする場合があります。
注: これは、異なるinvocationでベクトルメモリの異なる要素にアクセスし、少なくとも一方が書き込みアクセスの場合は同期が必要となることを意味します。§ 17.11 同期組み込み関数参照。
前提条件 | 結論 | 説明 |
---|---|---|
r: ref<AS,vecN<T>,AM> または ptr<AS,vecN<T>,AM> |
r.x : ref<AS,T,AM>r .r : ref<AS,T,AM> | メモリビューrが参照するベクトルの第1要素への参照を取得。 結果参照の元変数はrの元変数と同じ。 |
r: ref<AS,vecN<T>,AM> または ptr<AS,vecN<T>,AM> |
r.y : ref<AS,T,AM>r .g : ref<AS,T,AM> | メモリビューrが参照するベクトルの第2要素への参照を取得。 結果参照の元変数はrの元変数と同じ。 |
r: ref<AS,vecN<T>,AM> または ptr<AS,vecN<T>,AM> Nは3または4 |
r.z : ref<AS,T,AM>r .b : ref<AS,T,AM> | メモリビューrが参照するベクトルの第3要素への参照を取得。 結果参照の元変数はrの元変数と同じ。 |
r: ref<AS,vec4<T>,AM> または ptr<AS,vec4<T>,AM> |
r.w : ref<AS,T,AM>r .a : ref<AS,T,AM> | メモリビューrが参照するベクトルの第4要素への参照を取得。 結果参照の元変数はrの元変数と同じ。 |
r: ref<AS,vecN<T>,AM> または ptr<AS,vecN<T>,AM> i: i32 または u32 |
r[i] : ref<AS,T,AM> |
メモリビューrが参照するベクトルのi番目要素への参照を取得
iが範囲[0,N-1]外の場合:
結果参照の元変数はrの元変数と同じ。 |
8.5.2. 行列要素アクセス式
前提条件 | 結論 | 説明 |
---|---|---|
e: matCxR<T> i: i32 または u32 Tは具象型 | e[i]: vecR<T> |
eのi番目の列ベクトルを返す。
iが範囲[0,C-1]外の場合:
|
e: matCxR<T> i: i32 または u32 Tは抽象型 iはconst式 | e[i]: vecR<T> |
eのi番目の列ベクトルを返す。
iが範囲[0,C-1]外ならシェーダ生成エラー。 注: 抽象行列値eをconst式でない式でインデックスすると、インデックス適用前に具象化されます。 |
前提条件 | 結論 | 説明 |
---|---|---|
r: ref<AS,matCxR<T>,AM>
または ptr<AS,matCxR<T>,AM> i: i32 または u32 | r[i] : ref<AS,vecR<T>,AM> |
メモリビューrが参照する行列のi番目の列ベクトルへの参照を取得
iが範囲[0,C-1]外の場合:
結果参照の元変数はrの元変数と同じ。 |
8.5.3. 配列要素アクセス式
前提条件 | 結論 | 説明 |
---|---|---|
e: array<T,N> i: i32 または u32 Tは具象型 | e[i] : T |
e配列値のi番目の要素の値を返す。
iが範囲[0,N-1]外の場合:
|
e: array<T,N> i: i32 または u32 Tは抽象型 iはconst式 | e[i] : T |
e配列値のi番目の要素の値を返す。
iが範囲[0,N-1]外ならシェーダ生成エラー。 注: 抽象配列値eをconst式でない式でインデックスすると、インデックス適用前に具象化されます。 |
前提条件 | 結論 | 説明 |
---|---|---|
r: ref<AS,array<T,N>,AM> または ptr<AS,array<T,N>,AM> i: i32 または u32 | r[i] : ref<AS,T,AM> |
メモリビューrが参照する配列のi番目の要素への参照を取得
iが範囲[0,N-1]外の場合:
結果参照の元変数はrの元変数と同じ。 |
r: ref<AS,array<T>,AM> または ptr<AS,array<T>,AM> i: i32 または u32 | r[i] : ref<AS,T,AM> |
実行時サイズ配列のメモリビューrが参照する配列のi番目の要素への参照を取得
実行時に配列がN要素なら、iが範囲[0,N-1]外の場合は不正メモリ参照評価となる。 iが符号付き整数で0未満の場合:
結果参照の元変数はrの元変数と同じ。 |
8.5.4. 構造体要素アクセス式
前提条件 | 結論 | 説明 |
---|---|---|
Sは構造体型 MはSのメンバー名(型T) e: S | e.M: T | e構造体値のメンバー名Mの値を返す。 |
前提条件 | 結論 | 説明 |
---|---|---|
Sは構造体型 MはSのメンバー名(型T) r: ref<AS,S,AM> または ptr<AS,S,AM> | r.M: ref<AS,T,AM> | 構造体メモリビューから、識別子名Mの構造体メンバーへの参照を取得。 結果参照の元変数はrの元変数と同じ。 |
8.6. 論理式
前提条件 | 結論 | 説明 |
---|---|---|
e: T TはboolまたはvecN<bool> | ! e: T
| 論理否定。
eがfalse なら結果はtrue 、eがtrue ならfalse 。
Tがベクトルなら構成要素ごと。
|
前提条件 | 結論 | 説明 |
---|---|---|
e1: bool e2: bool | e1 || e2: bool
| 短絡"or"。e1またはe2がtrueなら結果はtrue 。
e1がfalseの場合のみe2を評価。
|
e1: bool e2: bool | e1 && e2: bool
| 短絡"and"。e1とe2がともにtrueなら結果はtrue 。
e1がtrueの場合のみe2を評価。
|
e1: T e2: T TはboolまたはvecN<bool> | e1 | e2: T
| 論理"or"。Tがベクトルなら構成要素ごと。e1とe2を両方評価。 |
e1: T e2: T TはboolまたはvecN<bool> | e1 & e2: T
| 論理"and"。Tがベクトルなら構成要素ごと。e1とe2を両方評価。 |
8.7. 算術式
前提条件 | 結論 | 説明 |
---|---|---|
e: T TはAbstractInt, AbstractFloat, i32, f32, f16, vecN<AbstractInt>, vecN<AbstractFloat>, vecN<i32>, vecN<f32>, または vecN<f16> | - e: T
| 符号反転。Tがベクトル型の場合構成要素ごとに適用。 Tが整数スカラー型でeが最小負値の場合、結果はe。 |
前提条件 | 結論 | 説明 |
---|---|---|
e1 : T e2 : T SはAbstractInt, AbstractFloat, i32, u32, f32, f16 TはSまたはvecN<S> | e1 + e2 : T
|
加算。Tがベクトル型の場合構成要素ごとに適用。
Tが浮動小数点型の場合、スカラー定義域は以下を除く拡張実数(x,y)の組:
|
e1 : T e2 : T SはAbstractInt, AbstractFloat, i32, u32, f32, f16 TはSまたはvecN<S> | e1 - e2 : T
|
減算。Tがベクトル型の場合構成要素ごとに適用。
Tが浮動小数点型の場合、スカラー定義域は以下を除く拡張実数(x,y)の組:
|
e1 : T e2 : T SはAbstractInt, AbstractFloat, i32, u32, f32, f16 TはSまたはvecN<S> | e1 * e2 : T
|
乗算。Tがベクトル型の場合構成要素ごとに適用。
Tが浮動小数点型の場合、スカラー定義域は以下を除く拡張実数(x,y)の組:
|
e1 : T e2 : T SはAbstractInt, AbstractFloat, i32, u32, f32, f16 TはSまたはvecN<S> | e1 / e2 : T
|
除算。Tがベクトル型の場合構成要素ごとに適用。
Tが符号付き整数スカラー型の場合:
注: 切り捨て動作の保証には符号なし除算より多くの演算が必要になる場合があります。 両被演算子が同符号と判明している場合は符号なし除算を使うこと。 Tが符号なし整数スカラー型の場合:
Tが浮動小数点型の場合、スカラー定義域は以下を除く拡張実数(x,y)の組:
|
e1 : T e2 : T SはAbstractInt, AbstractFloat, i32, u32, f32, f16 TはSまたはvecN<S> | e1 % e2 : T
|
剰余。Tがベクトル型の場合構成要素ごとに適用。
Tが符号付き整数スカラー型の場合、e1とe2を1回ずつ評価し、結果は:
注: 非ゼロの場合、結果はe1と同符号。 注: 一貫した動作保証には符号なし剰余より多くの演算が必要になる場合があります。 Tが符号なし整数スカラー型の場合、結果は:
Tが浮動小数点型の場合、結果はe1 - e2 * trunc(e1 / e2)。 Tが浮動小数点型の場合、スカラー定義域は以下を除く拡張実数(x,y)の組:
|
前提条件 | 結論 | 意味 |
---|---|---|
SはAbstractInt, AbstractFloat, f32, f16, i32, u32のいずれか VはvecN<S> es: S ev: V | ev + es: V
| ev + V(es)
|
es + ev: V
| V(es) + ev
| |
ev - es: V
| ev - V(es)
| |
es - ev: V
| V(es) - ev
| |
ev * es: V
| ev * V(es)
| |
es * ev: V
| V(es) * ev
| |
ev / es: V
| ev / V(es)
| |
es / ev: V
| V(es) / ev
| |
ev % es: V
| ev % V(es)
| |
es % ev: V
| V(es) % ev
|
前提条件 | 結論 | 意味 |
---|---|---|
e1, e2: matCxR<T> TはAbstractFloat, f32, f16 | e1 + e2: matCxR<T> | 行列加算:結果は構成要素ごとに計算され、結果の列iはe1[i] + e2[i] |
e1 - e2: matCxR<T>
| 行列減算:結果は構成要素ごとに計算され、結果の列iはe1[i] - e2[i] | |
m: matCxR<T> s: T TはAbstractFloat, f32, f16 | m * s: matCxR<T> | 構成要素ごとのスケーリング:(m *
s)[i][j]はm[i][j] * s
|
s * m: matCxR<T> | 構成要素ごとのスケーリング:(s *
m)[i][j]はm[i][j] * s
| |
m: matCxR<T> v: vecC<T> TはAbstractFloat, f32, f16 | m * v: vecR<T> | 線形代数の行列-列ベクトル積:
結果の構成要素iは
dot (transpose(m)[i],v)
|
m: matCxR<T> v: vecR<T> TはAbstractFloat, f32, f16 | v * m: vecC<T> | 線形代数の行ベクトル-行列積: transpose(transpose(m) *
transpose(v))
|
e1: matKxR<T> e2: matCxK<T> TはAbstractFloat, f32, f16 | e1 * e2: matCxR<T> | 線形代数の行列積。 |
8.8. 比較式
前提条件 | 結論 | 説明 |
---|---|---|
e1: T e2: T SはAbstractInt, AbstractFloat, bool, i32, u32, f32, f16 TはSまたはvecN<S> TBはTがベクトルの場合vecN<bool>、それ以外はbool | e1 == e2: TB
| 等価。Tがベクトル型の場合構成要素ごと。 |
e1: T e2: T SはAbstractInt, AbstractFloat, bool, i32, u32, f32, f16 TはSまたはvecN<S> TBはTがベクトルの場合vecN<bool>、それ以外はbool | e1 != e2: TB
| 非等価。Tがベクトル型の場合構成要素ごと。 |
e1: T e2: T SはAbstractInt, AbstractFloat, i32, u32, f32, f16 TはSまたはvecN<S> TBはTがベクトルの場合vecN<bool>、それ以外はbool | e1 < e2: TB
| より小さい。Tがベクトル型の場合構成要素ごと。 |
e1: T e2: T SはAbstractInt, AbstractFloat, i32, u32, f32, f16 TはSまたはvecN<S> TBはTがベクトルの場合vecN<bool>、それ以外はbool | e1 <= e2: TB
| 以下。Tがベクトル型の場合構成要素ごと。 |
e1: T e2: T SはAbstractInt, AbstractFloat, i32, u32, f32, f16 TはSまたはvecN<S> TBはTがベクトルの場合vecN<bool>、それ以外はbool | e1 > e2: TB
| より大きい。Tがベクトル型の場合構成要素ごと。 |
e1: T e2: T SはAbstractInt, AbstractFloat, i32, u32, f32, f16 TはSまたはvecN<S> TBはTがベクトルの場合vecN<bool>、それ以外はbool | e1 >= e2: TB
| 以上。Tがベクトル型の場合構成要素ごと。 |
8.9. ビット式
前提条件 | 結論 | 説明 |
---|---|---|
e: T SはAbstractInt, i32, u32のいずれか TはSまたはvecN<S> | ~ e : T
| eに対するビット単位の補数。 結果の各ビットはeの対応するビットの反転値。 Tがベクトル型の場合構成要素ごと。 |
前提条件 | 結論 | 説明 |
---|---|---|
e1: T e2: T SはAbstractInt, i32, u32のいずれか TはSまたはvecN<S> | e1 | e2: T
| ビット単位OR。Tがベクトル型の場合構成要素ごと。 |
e1: T e2: T SはAbstractInt, i32, u32のいずれか TはSまたはvecN<S> | e1 & e2: T
| ビット単位AND。Tがベクトル型の場合構成要素ごと。 |
e1: T e2: T SはAbstractInt, i32, u32のいずれか TはSまたはvecN<S> | e1 ^ e2: T
| ビット単位排他的OR。Tがベクトル型の場合構成要素ごと。 |
前提条件 | 結論 | 説明 |
---|---|---|
e1: T e2: TS Sはi32またはu32 TはSまたはvecN<S> TSはTがSのときu32、そうでなければvecN<u32> | e1 << e2: T
|
左シフト(シフト値は具体的な値):
e1を左シフトし、下位ビットにはゼロを挿入、上位ビットは破棄。 シフトするビット数はe2の値(e1のビット幅でmod)。
両方がシェーダ実行開始前に分かっている場合、結果はオーバーフローしない必要:
Tがベクトル型の場合構成要素ごと。 |
e1: T e2: TS TはAbstractIntまたはvecN<AbstractInt> TSはTがAbstractIntのときu32、そうでなければvecN<u32> | e1 << e2: T
|
左シフト(シフト値は抽象値):
e1を左シフトし、下位ビットにはゼロを挿入、上位ビットは破棄。 シフトするビット数はe2の値。 e1の最上位e2+1個のビットは必ず同じ値でなければならない。そうでない場合はオーバーフロー。 注: この条件は、破棄されるビットが元値の符号ビットと同じで、結果値の符号ビットとも同じである必要があることを意味します。 Tがベクトル型の場合構成要素ごと。 |
e1: T e2: TS Sはi32またはu32 TはSまたはvecN<S> TSはTがSのときu32、そうでなければvecN<u32> | e1 >> e2: T |
右シフト(シフト値は具体的な値)。
e1を右シフトし、下位ビットを破棄。 Sが符号なし型なら、上位ビットにはゼロを挿入。 Sが符号付き型なら:
シフトするビット数はe2(e1のビット幅でmod)。 e2がe1のビット幅以上の場合:
Tがベクトル型の場合構成要素ごと。 |
e1: T e2: TS TはAbstractIntまたはvecN<AbstractInt> TSはTがAbstractIntのときu32、そうでなければvecN<u32> | e1 >> e2: T |
右シフト(抽象値)。
e1を右シフトし、下位ビットを破棄。 e1が負なら挿入ビットは1、結果も負。そうでなければ0。 シフトするビット数はe2の値。 Tがベクトル型の場合構成要素ごと。 |
8.10. 関数呼び出し式
関数呼び出し式は、呼び出し先関数が戻り値型を持つ関数呼び出しを実行します。 呼び出し関数が値を返さない場合は、関数呼び出し文を使います。 § 9.5 関数呼び出し文参照。
8.11. 変数識別子式
前提条件 | 結論 | 説明 |
---|---|---|
vは識別子で、解決先が アドレス空間ASに宣言された 格納型T、 アクセスモードAMの スコープ内変数 | v: ref<AS,T,AM> | 変数名vのメモリへの参照が結果。 |
8.12. 仮引数式
前提条件 | 結論 | 説明 |
---|---|---|
aは識別子で、解決先が スコープ内の仮引数宣言(型T) | a: T | 関数呼び出し時の呼び出し元オペランドで渡された値。 |
8.13. アドレス取得式
アドレス取得演算子は参照を対応するポインタに変換します。
前提条件 | 結論 | 説明 |
---|---|---|
r: ref<AS,T,AM> |
& r: ptr<AS,T,AM>
|
結果は、参照値rと同じメモリビューに対応するポインタ値。
rが不正メモリ参照なら、結果ポインタも不正メモリ参照。 rがベクトル要素への参照のときシェーダ生成エラー。 |
8.14. 間接参照式
間接参照演算子はポインタを対応する参照に変換します。
前提条件 | 結論 | 説明 |
---|---|---|
p: ptr<AS,T,AM> |
* p: ref<AS,T,AM>
|
結果は、ポインタ値pと同じメモリビューに対応する参照値。
pが不正メモリ参照なら、結果参照も不正メモリ参照。 |
8.15. 値宣言の識別子式
前提条件 | 結論 | 説明 |
---|---|---|
cは識別子で、解決先が スコープ内のconst宣言(型T) | c: T | 初期化式を評価した値。 式はconst式で、シェーダ生成時に評価される。 |
cは識別子で、解決先が スコープ内のoverride宣言(型T) | c: T |
パイプライン生成時に定数IDに値が指定された場合、その値。
パイプラインごとに異なる場合あり。
それ以外の場合は初期化式の評価値。 パイプライン上書き可能定数はモジュールスコープに現れるので、評価はシェーダ実行開始前。 注:
API呼び出しで初期値未指定かつ |
cは識別子で、解決先が スコープ内のlet宣言(型T) | c: T | 初期化式を評価した値。
let宣言は関数本体内に現れ、初期化は制御フローが宣言に到達するたびに評価される。 |
8.16. 列挙式
前提条件 | 結論 | 説明 |
---|---|---|
eは識別子で、解決先が事前宣言列挙値 であり、列挙型Eに属する | e : E | § 6.3.1 事前宣言列挙値参照 |
8.17. 型式
前提条件 | 結論 | 説明 |
---|---|---|
tは識別子で、解決先が事前宣言型 | t : AllTypes | § 6.9 事前宣言型・型ジェネレーターまとめ参照 |
aは識別子で、解決先が型エイリアス | a : AllTypes | さらに、aはエイリアス先の型を表します。 |
sは識別子で、解決先が構造体型宣言 | s : AllTypes | さらに、sはその構造体型を表します。 |
tgは識別子で、解決先が型ジェネレーター
e1: T1 | tg _template_args_start e1, ..., eN _template_args_end : AllTypes |
各型ジェネレーターは必要なテンプレートパラメータの要件を独自に定めており、
テンプレートパラメータが結果型決定方法を定義します。
式e1~eNはテンプレートパラメータ。 例:型式 § 6.9 事前宣言型・型ジェネレーターまとめに型ジェネレーター一覧あり。 注: この2つのバリエーションは、eNの後にカンマがあるかどうかのみが異なります。 |
tg _template_args_start e1, ..., eN, _template_args_end : AllTypes |
8.18. 式文法まとめ
識別子がcall_phraseの最初のトークンの場合、次のいずれかです:
宣言とスコープの規則により、これらの名前は常に区別されます。
注: call_expression規則は、呼び出し式に型チェックを適用するために存在します。
expression ( ','
expression ) * ','
?
'['
expression ']'
component_or_swizzle_specifier ?
| multiplicative_expression multiplicative_operator unary_expression
| additive_expression additive_operator multiplicative_expression
| shift_expression _less_than shift_expression
| shift_expression _greater_than shift_expression
| shift_expression _less_than_equal shift_expression
| shift_expression _greater_than_equal shift_expression
binary_and_expression '&'
unary_expression
| short_circuit_or_expression '||'
relational_expression
8.19. 演算子の優先順位と結合規則
この節全体は非規範的です。
右辺WGSL式の演算子の優先順位と結合規則は、まとめると文法から現れます。右辺式は演算子をグループ化して整理し、次の図で示されるように構成されます:
可読性を高めるために、以下のグループは他のグループとは結合しません:
-
ショートサーキット OR (自身および関係と弱く結合可能)
-
ショートサーキット AND (自身および関係と弱く結合可能)
そして以下のグループは自身とは結合しません:
これらのグループセクション両方を結合するには、関係を明示的に設定するため括弧が必要です。次の例では、これらの規則がコメント内で式を無効にする場所を示します:
let a = x & ( y ^ ( z | w )); // 無効: x & y ^ z | w let b = ( x + y ) << ( z >= w ); // 無効: x + y << z >= w let c = x < ( y > z ); // 無効: x < y > z let d = x && ( y || z ); // 無効: x && y || z
優先順位によって式の暗黙の括弧が制御され、より強い結合の演算子は弱い優先順位の演算子と一緒に現れる場合、括弧で囲まれているかのように動作します。例えば、乗算演算子が加算より強い場合、式
a + b * c
から (a + (b * c))
が推論されます。同様に、結合規則によってこれらの暗黙の括弧の方向が制御されます。例えば、左から右への結合は
a + b + c
式から ((a + b) + c)
を推論し、右から左への結合は * * a
式から
(* (* a))
を推論します。
次の表は、演算子の優先順位、結合規則、結合対象をまとめており、強いものから弱いものへと並べています。結合対象欄には、各演算子のより強い式が記載されています。例えば「All above」と書かれている場合、この演算子はそれより強い式すべてを含むことができます。一方、「Unary」と書かれている場合、単項より弱くこの演算子より強いものは括弧が必要です。この欄は演算子を線形に並べるために必要です。
名前 | 演算子 | 結合規則 | 結合対象 |
---|---|---|---|
括弧付き | (...)
| ||
主 | a() , a[] , a.b
| 左から右 | |
単項 | -a , !a , ~a , *a , &a
| 右から左 | 上記すべて |
乗算 | a*b , a/b , a%b
| 左から右 | 上記すべて |
加算 | a+b , a-b
| 左から右 | 上記すべて |
シフト | a<<b , a>>b
| 括弧が必要 | 単項 |
関係 | a<b , a>b , a<=b , a>=b ,
a==b , a!=b
| 括弧が必要 | 上記すべて |
ビット単位 AND | a&b
| 左から右 | 単項 |
ビット単位 XOR | a^b
| 左から右 | 単項 |
ビット単位 OR | a|b
| 左から右 | 単項 |
ショートサーキット AND | a&&b
| 左から右 | 関係 |
ショートサーキット OR | a||b
| 左から右 | 関係 |
9. 文
文は、実行を制御するプログラムの断片です。 文は通常、順次実行されますが、制御フロー文によって非順次に実行される場合があります。
9.1. 複合文
複合文は、中括弧で囲まれた0個以上の文の並びです。 それらの文の中に宣言が含まれる場合、その識別子は、 次の文の開始から複合文の終わりまでスコープ内になります。
continuing_compound_statementは、複合文の特殊な形式であり、 continuing文の本体を構成し、最後にbreak-if文を任意で許容します。
9.2. 代入文
代入は、式を評価し、必要に応じてメモリに格納(つまり変数の内容を更新)します。
lhs_expression ( '='
| compound_assignment_operator ) expression
| '_'
'='
expression
演算子トークンの左側のテキストを左辺、 演算子トークンの右側の式を右辺と呼びます。
9.2.1. 単純代入
代入は、
単純代入である場合、
左辺が式であり、演算子がイコール('='
)トークンです。
この場合、右辺の値が左辺で参照されるメモリに書き込まれます。
事前条件 | 文 | 説明 |
---|---|---|
e: T, Tは具体的な構築可能型, r: ref<AS,T,AM>, ASは書き込み可能なアドレス空間, アクセスモード AMはwriteまたはread_write | r = e |
rを評価し、次にeを評価し、eで計算された値を
rが参照するメモリ位置に書き込みます。
注: 参照が無効なメモリ参照の場合、書き込みが実行されない場合や、 予期しないメモリ位置に書き込まれる場合があります。 |
最も単純な場合、左辺は変数名です。 その他のケースについては§ 6.4.8 参照値とポインタ値の形成を参照してください。
struct S { age : i32, weight : f32} var < private> person : S ; fn f () { var a : i32= 20 ; a = 30 ; // 'a' の内容を 30 に置き換える。 person . age = 31 ; // person 変数の age フィールドに 31 を書き込む。 var uv : vec2< f32> ; uv . y = 1.25 ; // uv の第2成分に 1.25 を格納。 let uv_x_ptr : ptr< function, f32> = & uv . x ; * uv_x_ptr = 2.5 ; // uv の第1成分に 2.5 を格納。 var sibling : S ; // person 変数の内容を sibling 変数にコピーする。 sibling = person ; }
9.2.2. ダミー代入
代入が
ダミー代入である場合、
左辺がアンダースコア('_'
)トークンです。
この場合、右辺は評価され、その後無視されます。
事前条件 | 文 | 説明 |
---|---|---|
e: T, Tは構築可能型、ポインタ型、テクスチャ型、またはサンプラー型 | _ = e |
eを評価します。
注: 得られた値は保存されません。
|
ダミー代入は以下のような用途で便利です:
-
戻り値がある関数を呼び出し、結果の値が不要であることを明確に表現する場合。
-
静的アクセスにより、変数を シェーダのリソースインターフェイスの一部として確立する場合。
注: バッファ変数のストア型が構築可能でない場合(例:atomic型やランタイムサイズ配列を含む場合)には、 その内容へのポインタを使ってください。
var < private> counter : i32; fn increment_and_yield_previous () -> i32{ let previous = counter ; counter = counter + 1 ; return previous ; } fn user () { // counter をインクリメントするが、結果は使わない。 _ = increment_and_yield_previous (); }
struct BufferContents { counter : atomic< u32> , data : array< vec4< f32>> } @group ( 0 ) @binding ( 0 ) var < storage> buf : BufferContents ; @group ( 0 ) @binding ( 1 ) var t : texture_2d< f32> ; @group ( 0 ) @binding ( 2 ) var s : sampler; @fragment fn shade_it () -> @location ( 0 ) vec4< f32> { // buf, t, s がシェーダインターフェイスの一部であることを宣言するが、何も利用しない。 _ = & buf ; _ = t ; _ = s ; return vec4< f32> (); }
9.2.3. 複合代入
代入は、 複合代入である場合、 左辺が式であり、 演算子がcompound_assignment_operatorsのいずれかです。
各文の型要件・意味論・挙動は、以下の表のように複合代入が展開されるものとして定義されています。ただし、
-
参照式e1は一度だけ評価され、
-
e1の参照型は 必ず read_writeの アクセスモードでなければなりません。
文 | 展開 |
---|---|
e1 += e2 | e1 = e1 + (e2) |
e1 -= e2 | e1 = e1 - (e2) |
e1 *= e2 | e1 = e1 * (e2) |
e1 /= e2 | e1 = e1 / (e2) |
e1 %= e2 | e1 = e1 % (e2) |
e1 &= e2 | e1 = e1 & (e2) |
e1 |= e2 | e1 = e1 | (e2) |
e1 ^= e2 | e1 = e1 ^ (e2) |
e1 >>= e2 | e1 = e1 >> (e2) |
e1 <<= e2 | e1 = e1 << (e2) |
注: 構文上、複合代入が ダミー代入になることはできません。
注: 参照e1は一度評価されますが、基となるメモリには2回アクセスされます。 まず読み取りアクセスで古い値を取得し、 次に書き込みアクセスで更新値を保存します。
var < private> next_item : i32= 0 ; fn advance_item () -> i32{ next_item += 1 ; // next_item に 1 を加算。 return next_item - 1 ; } fn bump_item () { var data : array< f32, 10 > ; next_item = 0 ; // data[0] に 5.0 を加算、advance_item() は一度だけ呼ばれる。 data [ advance_item ()] += 5.0 ; // この時点で next_item は 1。 } fn precedence_example () { var value = 1 ; // 複合代入の右辺は独立した式として評価される。 value *= 2 + 3 ; // value = value * (2 + 3); と同じ。 // 'value' は 5 になる。 }
e1+=
e2;
は次のように書き換えられます。
ここで識別子{ let p = &(
e1); *p = *p + (
e2); }
p
は、プログラム内の他の識別子と異なるものを選びます。
evは次のように書き換えられます。[
c] +=
e2;
ここで識別子{ let p = &(
ev); let c0 =
c; (*p)[c0] = (*p)[c0] + (
e2); }
c0
とp
は、プログラム内の他の識別子と異なるものを選びます。
9.3. インクリメント文とデクリメント文
インクリメント文は、変数の内容に1を加算します。 デクリメント文は、変数の内容から1を減算します。
この式は必ず 具体的な 整数スカラー ストア型とread_write アクセスモードを持つ参照として評価されなければなりません。
事前条件 | 文 | 説明 |
---|---|---|
r : ref<AS,T,read_write>, Tは具体的な 整数スカラー | r++
| rが参照するメモリ内容に1を加算します。
r += T(1) と同じ |
r : ref<AS,T,read_write>, Tは具体的な 整数スカラー | r--
| rが参照するメモリ内容から1を減算します。
r -= T(1) と同じ |
9.4. 制御フロー
制御フロー文によってプログラムが非順次に実行される場合があります。
9.4.1. if文
if文は、条件式の評価に基づいて最大1つの複合文を条件付きで実行します。
if
文はif
節、0個以上のelse if
節、任意のelse
節で構成されます。
'else'
'if'
expression compound_statement
'else'
compound_statement
型規則の事前条件:
各if
およびelse if
節の式は必ずbool型でなければなりません。
if
文の実行方法は以下の通りです:
-
if
節に関連付けられた条件を評価します。 結果がtrue
の場合、 制御は(条件式の直後の)最初の複合文に移ります。 -
そうでない場合、次の
else if
節(存在する場合)の条件をテキスト順で評価し、 結果がtrue
の場合、制御は対応する複合文に移ります。-
この挙動は、いずれかの条件が
true
になるまで、全てのelse if
節に対して繰り返されます。
-
-
どれも
true
と評価されない場合は、else
節(存在する場合)に関連付けられた複合文に制御が移ります。
9.4.2. switch文
switch文は、セレクタ式の評価に応じて、case節のいずれか、またはdefault節に制御を移します。
attribute * 'switch'
expression switch_body
attribute * '{'
switch_clause + '}'
'case'
case_selectors ':'
? compound_statement
'default'
':'
? compound_statement
case_selector ( ','
case_selector ) * ','
?
'default'
case節は、'case'
トークンの後にカンマ区切りのcaseセレクタのリストと、複合文形式の本体が続くものです。
default単独節は、'default'
トークンの後に、複合文形式の本体が続きます。
default節は、以下のいずれかです:
各switch文は必ず1つだけdefault節を持たなければなりません。
'default'
トークンは、1つのcase_selectorリスト内に複数回現れてはなりません。
型規則の事前条件: 1つのswitch文について、セレクタ式および全てのcaseセレクタ式は必ず同じ具体的な整数スカラー型でなければなりません。
case_selectors内の式は必ず 定数式である必要があります。
同じswitch文内の2つの異なるcaseセレクタ式が同じ値を持ってはなりません。
セレクタ値がcase_selectorリスト内の式の値と等しい場合、 制御はそのcase節の本体に移ります。 セレクタ値がどのcaseセレクタ値とも等しくない場合、制御はdefault節の本体に移ります。
節の本体の終わりに達すると、制御はswitch文の直後の最初の文に移ります。
節の本体内の文の1つが宣言である場合、 通常のスコープとライフタイム規則が適用されます。 すなわち、本体は文の並びであり、その中に宣言がある場合、その宣言のスコープは並びの次の文の開始から本体の終わりまで拡張します。 宣言は到達時に実行され、新しい変数または値のインスタンスを作成し、初期化します。
var a : i32; let x : i32= generateValue (); switch x { case 0 : { // コロンは省略可能 a = 1 ; } default { // defaultは最後でなくてもよい a = 2 ; } case 1 , 2 , { // 複数のセレクタ値を使える a = 3 ; } case 3 , { // 末尾のカンマは省略可能 a = 4 ; } case 4 { a = 5 ; } }
const c = 2 ; var a : i32; let x : i32= generateValue (); switch x { case 0 : { a = 1 ; } case 1 , c { // caseセレクタに定数式を使える a = 3 ; } case 3 , default { // defaultキーワードを他節と組み合わせ可能 a = 4 ; } }
9.4.3. loop文
loop文は、ループ本体を繰り返し実行します。 ループ本体は複合文として指定されます。 ループ本体の各実行を反復と呼びます。
この繰り返しは、breakまたは return文によって中断される場合があります。
オプションで、ループ本体の末尾にcontinuing文を配置できます。
動的エラーは、 loopが無限回の反復を実行する場合に発生します。 これはループの早期終了、他の非局所的な効果、あるいはデバイスロスにつながることがあります。
ループ本体内の文の1つが宣言である場合、 通常のスコープとライフタイム規則が適用されます。 すなわち、ループ本体は文の並びであり、その中に宣言がある場合、その宣言のスコープは並びの次の文の開始からループ本体の終わりまで拡張します。 宣言は到達するたびに実行されるため、各反復ごとに新しい変数または値のインスタンスが作成され、再初期化されます。
注:
loop文は特殊な構文であり、一般的にはfor
やwhile
文を使うことを推奨します。loop文は他のシェーダ言語との最大の違いのひとつです。
この設計は、コンパイル済みコードでよく見られるループイディオムを直接表現しています。 特に、ループ更新文をループ本体の末尾に配置することで、本体内で定義された値を自然に利用できます。
- <1> 初期化はループの前に記述されています。
var a : i32= 2 ; let step : i32= 1 ; for ( var i : i32= 0 ; i < 4 ; i += step ) { if ( i % 2 == 0 ) { continue ; } a *= 2 ; }
var a : i32= 2 ; var i : i32= 0 ; loop { if i >= 4 { break ; } let step : i32= 1 ; i = i + step ; if i % 2 == 0 { continue ; } a = a * 2 ; }
var a : i32= 2 ; var i : i32= 0 ; loop { if i >= 4 { break ; } let step : i32= 1 ; if i % 2 == 0 { continue ; } a = a * 2 ; continuing { // <2> i = i + step ; } }
- <2> continue構文は
loop
の末尾に記述されています。
9.4.4. for文
attribute * 'for'
'('
for_header ')'
compound_statement
for_init ? ';'
expression ? ';'
for_update ?
for ステートメントは、複合ステートメント に含まれる
ループ ステートメントの構文糖衣です。
一般的に、for
ステートメントは以下の形式を取ります。
for (
initializer;
condition;
update_part) {
body}
条件式が存在する場合、for
ステートメントは次の形式のループに展開されます:
{条件式が存在しない場合、
initializer ;
loop {
if !(condition) { break; }
body
continuing { update_part }
}
}
for
ステートメントは次の形式のループに展開されます:
{
initializer ;
loop {
body
continuing { update_part }
}
}
さらに:
-
initializer
が空でない場合、最初の スコープ 内で追加的に実行され、最初の イテレーション の前に実行されます。 initializer 内の宣言のスコープは、ループ本体の終了まで拡張されます。 -
型ルールの事前条件: 条件が空でない場合、必須として bool 型の式である必要があります。
-
条件が存在する場合、ループ本体を実行する直前に評価されます。 条件が false の場合、§ 9.4.6 Break ステートメント が実行され、ループの実行を終了します。 このチェックは各ループイテレーションの開始時に行われます。
-
-
update_part
が空でない場合、ループ構造の最後に continuing ステートメントとなります。 -
デシュガリングの 過程で、
body
内で宣言された識別子は、update_part
内の全ての識別子がデシュガリング前と同じ宣言に 解決 されるように必要に応じて名前が変更されます。
for
ループの initializer
は、ループが実行される前に一度だけ実行されます。
宣言 が initializer に現れる場合、その
識別子 は
スコープ内 にあり、body
の終了まで有効です。
body
内の宣言とは異なり、この宣言は各イテレーションで再初期化されません。
condition
、body
、および update_part
はこの順序で実行され、ループの
イテレーション を形成します。
body
は特別な形式の 複合ステートメント です。
body
内の宣言の識別子は、次のステートメントの開始からbody
の終了まで
スコープ内 にあります。
この宣言は到達するたびに実行されるため、各新しいイテレーションは変数または定数の新しいインスタンスを作成し、それを再初期化します。
var a : i32= 2 ; for ( var i : i32= 0 ; i < 4 ; i ++ ) { if a == 0 { continue ; } a = a + 2 ; }
変換後:
var a : i32= 2 ; { // i 変数用に新しいスコープを導入 var i : i32= 0 ; loop { if ! ( i < 4 ) { break ; } if a == 0 { continue ; } a = a + 2 ; continuing { i ++ ; } } }
var a : i32= 2 ; for ( var i : i32= 0 ; ; i ++ ) { if a == 0 { continue ; } if i == 4 { break ; } a = a + 2 ; }
変換後:
var a : i32= 2 ; { // i 変数用に新しいスコープを導入 var i : i32= 0 ; loop { // 注意: デシュガリングで if 文は追加されません。 if a == 0 { continue ; } if i == 4 { break ; } a = a + 2 ; continuing { i ++ ; } } }
動的エラーは、 for ループが無制限の イテレーション を実行する場合に発生します。 これにより、ループの早期終了、他の非局所的影響、または デバイスの喪失 さえ引き起こされる可能性があります。
9.4.5. while文
attribute * 'while'
expression compound_statement
while文は、条件でパラメータ化されるループの一種です。 各ループ反復の開始時に、 真偽値条件が評価されます。 条件が偽のとき、whileループは実行を終了します。 そうでない場合は反復の残りが実行されます。
型規則の事前条件: 条件は必ずbool型でなければなりません。
whileループはloop文またはfor文の構文糖とみなせます。 以下の文形式は同等です:
-
while
condition{
body_statements}
-
loop { if !
condition{break;}
body_statements}
-
for (;
condition;) {
body_statements}
動的エラーは、 whileループが無限回の反復を実行する場合に発生します。 これはループの早期終了、他の非局所的な効果、あるいはデバイスロスにつながることがあります。
9.4.6. break文
'break'
break文は、最も近い囲みのループ またはswitch文の本体直後へ制御を移し、 そのループまたはswitch文の実行を終了します。
break
文は必ず
loop、
for、
while、
およびswitch文の中だけで使われなければなりません。
break
文は、ループのcontinuing文から抜け出すような位置に置いてはなりません。
代わりにbreak-if文を使用してください。
var a : i32= 2 ; var i : i32= 0 ; loop { let step : i32= 1 ; if i % 2 == 0 { continue ; } a = a * 2 ; continuing { i = i + step ; if i >= 4 { break ; } // 無効。代わりに break-if を使用すること。 } }
9.4.7. break-if文
'break'
'if'
expression ';'
break-if文は真偽値条件を評価します。 条件がtrueであれば、最も近い囲みのloop文の本体直後に制御が移り、そのループの実行が終了します。
型規則の事前条件: 条件は必ずbool型でなければなりません。
注: break-if文はcontinuing 文の本体の最後の文としてのみ現れることができます。
var a : i32= 2 ; var i : i32= 0 ; loop { let step : i32= 1 ; if i % 2 == 0 { continue ; } a = a * 2 ; continuing { i = i + step ; break if i >= 4 ; } }
9.4.8. continue文
'continue'
continue文は最も近い囲みのloop文において以下のように制御を移します:
-
そのループ本体の末尾にcontinuing文があれば、そこに進みます。
-
なければ、ループ本体の最初の文に戻り、次の反復を開始します。
continue
文は必ずループ、forまたはwhile文の中でのみ使用しなければなりません。
continue
文は絶対に外側のcontinuing文へ制御を移すような位置に置いてはいけません。
(continuing
文への分岐は前方分岐です。)
continue
文は絶対に対象となるcontinuing文で使用される宣言を飛ばして制御を移す位置に置いてはいけません。
注:
continue
はcontinuing
文内で使用する場合、
continuing
文の中にネストされた別のループ内から制御フローを移す場合のみ使えます。
つまり、現在実行中のcontinuing
文の先頭へ制御を移すためにcontinue
を使うことはできません。
var i : i32= 0 ; loop { if i >= 4 { break ; } if i % 2 == 0 { continue ; } // <3> let step : i32= 2 ; continuing { i = i + step ; } }
- <3>
continue
はcontinuing
句で使われているstep
宣言を飛ばすため無効です
9.4.9. continuing文
'continuing'
continuing_compound_statement
continuing文は、ループ反復の末尾で実行される複合文を指定します。 この構文は任意です。
複合文は、どのネストレベルでもreturnを含んではなりません。
9.4.10. return文
'return'
expression ?
return文は現在の関数の実行を終了します。 関数がエントリポイントの場合、 現在のシェーダ呼び出しが終了します。 それ以外の場合は、現在の関数呼び出しの呼び出し箇所での評価が続行されます。
関数が戻り値型を持たない場合、return文は任意です。その場合、return文に値を指定してはなりません。 それ以外の場合は、式が必ず存在し、これを戻り値と呼びます。 この場合、関数呼び出し箇所は戻り値として評価されます。 戻り値の型は関数の戻り値型と一致しなければなりません。
9.4.11. discard文
discard文は呼び出しを
ヘルパー呼び出しに変換し、フラグメントを破棄します。
discard
文は必ず フラグメントシェーダ段階でのみ使われなければなりません。
より厳密には、discard
文の実行は次のことを行います:
-
現在の呼び出しをヘルパー呼び出しに変換し、
-
現在のフラグメントがGPURenderPipelineで下流処理されるのを防ぎます。
discard文の前に実行された文のみが観測可能な効果を持ちます。
注: discard
文は
フラグメント段階の任意の関数で実行でき、効果は同じです:
フラグメントは破棄されます。
@group ( 0 ) @binding ( 0 ) var < storage, read_write> will_emit_color : u32; fn discard_if_shallow ( pos : vec4< f32> ) { if pos . z < 0.001 { // ここが実行される場合、will_emit_color変数は1にはならない。 // ヘルパー呼び出しはshared memoryに書き込まないため。 discard ; } will_emit_color = 1 ; } @fragment fn main ( @builtin ( position) coord_in : vec4< f32> ) -> @location ( 0 ) vec4< f32> { discard_if_shallow ( coord_in ); // ヘルパー関数がdiscard文を実行しなかった場合のみ、値1をセットし赤色を出す。 will_emit_color = 1 ; return vec4< f32> ( 1.0 , 0.0 , 0.0 , 1.0 ); }
9.5. 関数呼び出し文
関数呼び出し文は関数呼び出しを実行します。
呼び出される関数にmust_use属性がある場合、シェーダ生成エラーとなります。
注: 関数が値を返す場合でも、 その関数にmust_use属性がなければ、 その値は無視されます。
9.6. 文の文法まとめ
statement規則は、関数本体内のほとんどの場所で使える文に一致します。
さらに、特定の文は非常に特定の文脈でのみ使用できます:
9.7. 文の挙動解析
9.7.1. 規則
制御フローに影響する一部の文は、特定のコンテキストでのみ有効です。 例えば、continue は loop、for、または while の外では無効です。 さらに、均一性解析(§ 15.2 Uniformity参照)では、制御フローが複数の異なる方法で文を抜ける場合を判定する必要があります。
これら両方の目的は、文の実行挙動を要約するシステムによって達成されます。挙動解析は各文を、文の評価が完了した後に実行が進む可能な方法の集合へとマッピングします。 値や式に対する型解析と同様に、挙動解析もボトムアップで進行します:まず基本的な文の挙動を判定し、それから上位の構造に対して結合規則を適用して挙動を決定します。
挙動とは、次の要素を持つ集合です:
-
Return
-
Break
-
Continue
-
Next
これらは複合文を抜ける方法に対応しており、キーワードによるものか、次の文へ進む("Next")ことによるものです。
「s: B」は、s が挙動に関する規則を満たし、挙動 B を持つことを表します。
各関数について:
-
その本体はこれらの規則により有効な文でなければならない。
各関数には挙動を割り当てます:それは本体の挙動(本体を通常の文として扱う)、ただし "Return" は "Next" に置換されます。 上記の規則の結果として、関数挙動は常に {} または {Next} のいずれかになります。
挙動解析は、各文および関数について空でない挙動を判定できなければならない。
文 | 前提条件 | 結果の挙動 |
---|---|---|
空文 | {Next} | |
{s} | s: B | B |
s1 s2
注: s1 はしばしばセミコロンで終わる。 | s1: B1 B1 に Next がある s2: B2 | (B1∖{Next}) ∪ B2 |
s1: B1 B1 に Next がない s2: B2 | B1 | |
var x:T; | {Next} | |
let x = e; | {Next} | |
var x = e; | {Next} | |
x = e; | {Next} | |
_ = e; | {Next} | |
f(e1, ..., en); | f の挙動 B がある | B |
return; | {Return} | |
return e; | {Return} | |
discard; | {Next} | |
break; | {Break} | |
break if e; | {Break, Next} | |
continue; | {Continue} | |
const_assert e; | {Next} | |
if e s1 else s2 |
s1: B1 s2: B2 | B1 ∪ B2 |
loop {s1 continuing {s2}} |
s1: B1 s2: B2 B1 = {Return} B2 に {Continue, Return} がない | {Return} |
s1: B1 s2: B2 B1 ≠ {Return} B2 に {Continue, Return} がない (B1 ∪ B2) に Break がない | (B1 ∪ B2)∖{Continue, Next} | |
s1: B1 s2: B2 B1 ≠ {Return} B2 に {Continue, Return} がない (B1 ∪ B2) に Break がある | (B1 ∪ B2 ∪ {Next})∖{Break, Continue} | |
switch e {case c1: s1 ... case cn: sn} |
s1: B1 ... sn: Bn (B1 ∪ ... ∪ Bn) に Break がない | B1 ∪ ... ∪ Bn |
s1: B1 ... sn: Bn (B1 ∪ ... ∪ Bn) に Break がある | (B1 ∪ ... ∪ Bn ∪ {Next})∖Break |
注: ∪は集合の和演算、∖は集合の差分演算です。
注: 空文の場合は、loop
の本体が空の場合や、for
ループに初期化や更新文がない場合に該当します。
この解析の目的のため:
-
for
ループはデシュガー処理される(§ 9.4.4 For Statement参照) -
while
ループはデシュガー処理される(§ 9.4.5 While Statement参照) -
loop {s}
はloop {s continuing {}}
として扱う -
else
分岐のないif
文は空の else 分岐(else {}
)があるものとして扱う;これにより 挙動 に Next が加わる -
else if
分岐を持つif
文は、入れ子の単純なif/else
文として扱う -
switch_clause が
default
で始まる場合は、switch_clause がcase _:
で始まる場合と同様に動作する
各built-in functionは{Next}のbehaviorを持つ。 また、上記の表に記載されていない各演算子の適用も、同じオペランドを持つ関数呼び出しとして、関数の{Next}のbehaviorを持つかのような同じbehaviorを持つ。
関数のmustは、上記の規則を満たさなければならない。
Note: 式のbehaviorの解析は不要である。なぜなら、式は常に{Next}であるか、既に解析された関数がエラーを発生させているためである。
9.7.2. 補足
この節は情報提供のみであり、規範的ではありません。
挙動解析によって、プログラムは以下のような理由で拒否されることがあります (上記の要件を再掲):
-
関数の本体(通常の文として扱う)が{Next, Return}に含まれない挙動を持つ場合。
-
戻り値型を持つ関数の本体が{Return}でない挙動を持つ場合。
-
continuingブロックの挙動にContinueやReturnが含まれる場合。
-
明らかに無限ループとなるものが空集合の挙動セットとなり、無効となる場合。
この解析は、呼び出しグラフをボトムアップで解析することで線形時間で実行できます(関数呼び出しの挙動は関数のコードに依存するため)。
9.7.3. 例
以下は、この解析の実例です:
fn simple () -> i32{ var a : i32; return 0 ; // 挙動: {Return} a = 1 ; // 有効、静的に到達不可能なコード。 // 文の挙動: {Next} // 全体の挙動(逐次文による): {Return} return 2 ; // 有効、静的に到達不可能なコード。挙動: {Return} } // 関数の挙動: {Return}
fn nested () -> i32{ var a : i32; { // 複合文の開始。 a = 2 ; // 挙動: {Next} return 1 ; // 挙動: {Return} } // 複合文全体の挙動は{Return} a = 1 ; // 有効、静的に到達不可能なコード。 // 文の挙動: {Next} // 全体の挙動(逐次文による): {Return} return 2 ; // 有効、静的に到達不可能なコード。挙動: {Return} }
fn if_example () { var a : i32= 0 ; loop { if a == 5 { break ; // 挙動: {Break} } // if複合文全体の挙動: {Break, Next}, // ifには暗黙的な空のelseがある a = a + 1 ; // 有効、前の文の挙動に"Next"があるため } }
fn if_example () { var a : i32= 0 ; loop { if a == 5 { break ; // 挙動: {Break} } else { continue ; // 挙動: {Continue} } // if複合文全体の挙動: {Break, Continue} a = a + 1 ; // 有効、静的に到達不可能なコード。 // 文の挙動: {Next} // 全体の挙動: {Break, Continue} } }
fn if_example () { var a : i32= 0 ; loop { // if e1 s1 else if e2 s2 else s3 // は次と同一: // if e1 else { if e2 s2 else s3 } if a == 5 { break ; // 挙動: {Break} } else if a == 42 { continue ; // 挙動: {Continue} } else { return ; // 挙動: {Return} } // if複合文全体の挙動: // {Break, Continue, Return} } // ループ複合文全体の挙動 {Next, Return} } // 関数全体の挙動 {Next}
fn switch_example () { var a : i32= 0 ; switch a { default : { break ; // 挙動: {Break} } } // 挙動: {Next}、switchはBreakをNextに置き換える a = 5 ; // 有効、前の文にNextがあるため }
fn invalid_infinite_loop () { loop { discard ; // 挙動 { Next } } // 無効、ループ全体の挙動は{ }。 }
fn conditional_continue () { var a : i32; loop { if a == 5 { break ; } // 挙動: {Break, Next} if a % 2 == 1 { // 有効、前の文にNextがあるため continue ; // 挙動: {Continue} } // 挙動: {Continue, Next} a = a * 2 ; // 有効、前の文にNextがあるため continuing { // 有効、continuing文の挙動は{Next} // これに{Break, Continue, Return}は含まれない a = a + 1 ; } } // ループ全体の挙動は{Next}、 // "Continue"と"Next"は吸収され、 // "Break"は"Next"に置き換えられる }
fn redundant_continue_with_continuing () { var a : i32; loop { if a == 5 { break ; } continue ; // 有効。冗長で、次の文へ分岐するだけ。 continuing { a = a + 1 ; } } }
fn continue_end_of_loop_body () { for ( var i : i32= 0 ; i < 5 ; i ++ ) { continue ; // 有効。冗長で、 // ループ本体の末尾に分岐するだけ。 } // 挙動: {Next}、 // ループは"Continue"を吸収し、 // "for"ループは必ず"Next"を加える }
for
ループはloop
と条件付きbreakにデシュガーされます。前述の例の通り、条件付きbreakは挙動 {Break,
Next}を持ち、これによりループの挙動に"Next"が加えられます。
fn missing_return () -> i32{ var a : i32= 0 ; if a == 42 { return a ; // 挙動: {Return} } // 挙動: {Next, Return} } // エラー: Nextは戻り値型を持つ関数の本体には無効 //
fn continue_out_of_loop () { var a : i32= 0 ; if a > 0 { continue ; // 挙動: {Continue} } // 挙動: {Next, Continue} } // エラー: 関数本体内ではContinueは無効
continue
をbreak
に置き換えても、同じ理由で無効です。
10. アサーション
アサーションは、 ブール条件が満たされていることを検証するためのチェックです。
WGSLでは、アサーションの種類は一つ、 定数アサーションのみ定義されています。
'const_assert'
expression
型規則の前提条件: 式は必ずbool型でなければなりません。
10.1. 定数アサーション文
定数アサーション文はアサーションの一種であり、
式がfalse
となった場合、シェーダー生成エラーを発生させます。
式は必ず定数式でなければなりません。
この文は、シェーダー内で静的アクセス条件を満たすことができますが、コンパイル後のシェーダーに対しては他に効果はありません。
定数アサーションはモジュールスコープまたは関数スコープの文として記述できます。
const x = 1 ; const y = 2 ; const_assert x < y ; // モジュールスコープで有効。 const_assert ( y != 0 ); // 括弧は省略可能。 fn foo () { const z = x + y - 2 ; const_assert z > 0 ; // 関数内で有効。 let a = 3 ; const_assert a != 0 ; // 無効、式は定数式でなければならない。 }
11. 関数
関数は、呼び出されたときに計算処理を実行します。
関数は次のいずれかの方法で呼び出されます:
-
関数呼び出し式の評価による。§ 8.10 関数呼び出し式参照。
-
関数呼び出し文の実行による。§ 9.5 関数呼び出し文参照。
-
エントリーポイント関数は、WebGPUの実装によって シェーダーステージの処理を パイプライン内で実行するために呼び出されます。§ 13 エントリーポイント参照。
WGSLの関数は、定義順序に制限がなく、使用より後に定義しても構いません。 そのため、関数プロトタイプや前方宣言は不要であり、その方法もありません。
関数には2種類あります:
-
組み込み関数はWGSLの実装によって提供され、 常にWGSLモジュールで利用可能です。 § 17 組み込み関数参照。
-
ユーザー定義関数はWGSLモジュール内で宣言されます。
11.1. ユーザー定義関数の宣言
関数宣言は、ユーザー定義関数を作成します。指定内容は以下の通りです:
-
オプションの属性のセット。
-
関数名。
-
仮引数リスト:0個以上の仮引数宣言の順序付き列。 それぞれに属性を付与でき、カンマ区切りで括弧で囲みます。
-
オプションの戻り値型(属性付与可)。
-
関数本体。 これは関数が呼び出されたときに実行される文の集合です。
関数宣言は必ずモジュールスコープでのみ記述可能です。 関数名はプログラム全体でスコープ内となります。
注: 各ユーザー定義関数は オーバーロードを1つしか持ちません。
仮引数の宣言は、関数呼び出し時に値として 必要となる識別子名と型を指定します。 仮引数には属性を付与できます。 § 11.2 関数呼び出し参照。 識別子のスコープは関数本体です。 同一関数の2つの仮引数は、同じ名前を持ってはなりません。
注: 一部の組み込み関数では、抽象数値型の引数が許可される場合があります。 ただし、ユーザー宣言関数ではこの機能は現時点ではサポートされていません。
戻り値型を指定する場合は、必ず構築可能でなければなりません。
WGSLでは、関数宣言に付与できる属性は以下の通りです:
WGSLでは、関数の仮引数・戻り値型に付与できる属性は以下の通りです:
'fn'
ident '('
param_list ? ')'
( '->'
attribute * template_elaborated_ident ) ?
// add_two関数の宣言。 // 仮引数は i と b の2つ。 // 戻り値型は i32。 // 本体はreturn文を持つ。 fn add_two ( i : i32, b : f32) -> i32{ return i + 2 ; // 仮引数は本体内で利用可能。 } // コンピュートシェーダーのエントリーポイント関数 'main'。 // 戻り値型は指定なし。 // add_two関数を呼び出し、 // 結果を'six'という値に格納。 @compute @workgroup_size ( 1 ) fn main () { let six : i32= add_two ( 4 , 5.0 ); }
11.2. 関数呼び出し
関数呼び出しは、 関数を実行する文または式です。
関数呼び出しを含む関数は、呼び出し元関数または呼び出し元と呼ばれます。 実際に呼び出される関数は呼び出し先関数または呼び出し先です。
関数呼び出しは:
-
呼び出し先関数の名前を指定し、
-
括弧で囲み、カンマ区切りの引数値式のリストを与えます。
関数呼び出しは、必ず仮引数の数と 同じだけの引数値を呼び出し先関数に与える必要があります。 各引数値は、位置対応する仮引数と同じ型に必ず評価されなければなりません。
まとめると、関数呼び出し時は:
呼び出し先関数は次のようにreturnされます:
詳細な手順は:
-
関数呼び出しの引数値が評価されます。 評価順は左から右です。
-
呼び出し先がユーザー定義の場合、 呼び出し先関数の関数スコープ変数ごとにメモリが割り当てられます。
-
初期化は § 7.3 var宣言で記載された通りに行われます。
-
-
呼び出し先関数の仮引数には、位置で一致する関数呼び出しの引数値が割り当てられます。 例えば、呼び出し先関数の最初の仮引数は、呼び出し元の最初の引数値となります。
-
呼び出し先関数へ制御が移ります。 ユーザー定義関数の場合、本体の最初の文から実行されます。
-
呼び出し先関数はreturnされるまで実行されます。
-
制御が呼び出し元関数に戻り、呼び出し先の実行が解除されます。 戻り値を持つ場合、その値が関数呼び出し式の値となります。
関数呼び出しの位置は、呼び出し位置と呼ばれます。これは、トークンのうち call_phrase構文規則の解析インスタンスで最初のものの位置です。 呼び出し位置は動的コンテキストの一種です。 そのため、同じテキスト上の位置が複数の呼び出し位置となることもあります。
注: フラグメントシェーダー内の関数呼び出しが、クアッド内の全ての呼び出しが discardされた場合、 returnしない可能性があります。 この場合、制御は呼び出し元関数に戻りません。
11.3. const
関数
const属性付きで宣言された関数は、 シェーダー生成時に評価できます。 これらの関数は定数関数と呼ばれます。 これらへの呼び出しは定数式の一部として利用できます。
関数内に定数式でない 式やconst宣言でない宣言が含まれている場合、 シェーダー生成エラーとなります。
注: const属性は ユーザー定義関数には付けられません。
const first_one = firstLeadingBit ( 1234 + 4567 ); // 12に評価される // first_oneの型はi32、 // firstLeadingBitはAbstractIntに対応しないため @id ( 1 ) override x : i32; override y = firstLeadingBit ( x ); // 定数式は // override式でも利用可能 // firstLeadingBit(x)はこの文脈では定数式ではない fn foo () { var a : array< i32, firstLeadingBit ( 257 ) > ; // 定数関数は // パラメータがすべて定数式なら // 定数式内で利用可能 }
11.4. 関数に対する制約
-
関数パラメータは 必ず次のいずれかの型でなければなりません:
-
構築可能型
-
ポインター型
-
テクスチャ型
-
サンプラー型
-
-
各関数呼び出しの引数は、必ず 対応する関数パラメータの型に評価されなければなりません。
注: 再帰はどの宣言種にも循環が許可されていないため禁止されています。
fn bar ( p : ptr< function, f32> ) { } fn baz ( p : ptr< private, i32> ) { } fn bar2 ( p : ptr< function, f32> ) { let a = &*&* ( p ); bar ( p ); // 有効 bar ( a ); // 有効 } fn baz2 ( p : ptr< storage, f32> ) { } struct S { x : i32} @group ( 0 ) @binding ( 0 ) var < storage> ro_storage : f32; @group ( 0 ) @binding ( 1 ) var < storage, read_write> rw_storage : f32; var usable_priv : i32; var unusable_priv : array< i32, 4 > ; fn foo () { var usable_func : f32; var unusable_func : S ; var i32_func : i32; let a_priv = & usable_priv ; let b_priv = a_priv ; let c_priv = &*& usable_priv ; let d_priv = & ( unusable_priv . x ); let e_priv = d_priv ; let a_func = & usable_func ; let b_func = & unusable_func ; let c_func = & ( * b_func )[ 0 ]; let d_func = c_func ; let e_func = &* a_func ; baz ( & usable_priv ); // 有効、変数のアドレス取得 baz ( a_priv ); // 有効、実質的に変数のアドレス取得 baz ( b_priv ); // 有効、実質的に変数のアドレス取得 baz ( c_priv ); // 有効、実質的に変数のアドレス取得 baz ( d_priv ); // 有効、メモリビューが変化 baz ( e_priv ); // 有効、メモリビューが変化 baz ( & i32_func ); // 無効、アドレス空間不一致 bar ( & usable_func ); // 有効、変数のアドレス取得 bar ( c_func ); // 有効、メモリビューが変化 bar ( d_func ); // 有効、メモリビューが変化 bar ( e_func ); // 有効、実質的に変数のアドレス取得 baz2 ( & ro_storage ); // 有効、変数のアドレス取得 baz2 ( & rw_storage ); // 無効、アクセスモード不一致 }
11.4.1. エイリアス解析
11.4.1.1. ルート識別子
メモリ位置は、関数の実行中にメモリビューを使ってアクセスできます。 関数内では、各メモリビューに特定のルート識別子が存在し、 その関数内で最初にそのメモリへのアクセスを提供する変数または仮引数の名前となります。
関数内で参照型や ポインター型の局所的な式は、 あるルート識別子のための新しい名前を導入することがありますが、 各式には静的に決定可能なルート識別子があります。
式Eがポインター型または参照型の場合、 ルート識別子は 以下のように見つかる元の変数か ポインター型の仮引数です:
-
Eが変数に解決される識別子の場合、そのルート識別子はその変数です。
-
Eがポインター型の仮引数に解決される識別子の場合、そのルート識別子はその仮引数です。
-
Eが
(
E2)
、&
E2、*
E2、 またはE2[
Ei]
の形の場合、そのルート識別子はE2のルート識別子です。 -
EがE2.swizという形のベクターアクセス式で、 swizがスウィズル名の場合、そのルート識別子はE2のルート識別子です。
-
EがE2.member_nameという形の構造体アクセス式の場合、 ルート識別子はE2のルート識別子です。
11.4.1.2. 別名化(エイリアシング)
元の変数は、 ルート識別子に対する動的な概念であり、 関数の呼び出し位置に依存しますが、 WGSLモジュールは静的解析によって、各ルート識別子に対する全ての可能な元の変数の集合を決定できます。
2つのルート識別子が 同じ元の変数であれば、 それらは別名(エイリアス)となります。 WGSL関数の実行は、1つが書き込みで、もう1つが読み込みまたは書き込みとなる 別名化されたルート識別子を介してメモリにアクセスする可能性がある場合、 絶対に許可されません。 これは、呼び出しグラフの葉から上方向(すなわちトポロジカル順)に解析することで判定されます。 各関数に対し、以下の集合を記録します:
-
ポインタ型パラメータで、メモリビューのルート識別子として使われ、当該関数または呼び出し先関数で書き込みされるもの。
-
ポインタ型パラメータで、メモリビューのルート識別子として使われ、当該関数または呼び出し先関数で読み出しされるもの。
関数の各呼び出し位置において、 以下のいずれかが起きる場合はシェーダー生成エラーとなります:
-
ポインタ型の2つの引数が同じルート識別子を持ち、いずれかの対応するパラメータが書き込みパラメータ集合に含まれている場合。
-
ポインタ型の引数のルート識別子がモジュールスコープ変数で、次のいずれかの場合:
-
対応するポインタパラメータが書き込みポインタ集合に含まれ、
-
そのモジュールスコープ変数が呼び出し先関数の読み出し集合に含まれている。
-
-
ポインタ型の引数のルート識別子がモジュールスコープ変数で、次のいずれかの場合:
-
対応するポインタパラメータが書き込みポインタ集合に含まれ、
-
そのモジュールスコープ変数が呼び出し先関数の書き込み集合に含まれている。
-
-
ポインタ型の引数のルート識別子がモジュールスコープ変数で、次のいずれかの場合:
-
対応するポインタパラメータが読み出しポインタ集合に含まれ、
-
そのモジュールスコープ変数が呼び出し先関数の書き込み集合に含まれている。
-
var < private> x : i32= 0 ; fn f1 ( p1 : ptr< function, i32> , p2 : ptr< function, i32> ) { * p1 = * p2 ; } fn f2 ( p1 : ptr< function, i32> , p2 : ptr< function, i32> ) { f1 ( p1 , p2 ); } fn f3 () { var a : i32= 0 ; f2 ( & a , & a ); // 無効。2つのポインタパラメータに同じルート識別子を渡してはならない // (サブ関数で書き込みがある場合も含む)。 } fn f4 ( p1 : ptr< function, i32> , p2 : ptr< function, i32> ) -> i32{ return * p1 + * p2 ; } fn f5 () { var a : i32= 0 ; let b = f4 ( & a , & a ); // 有効。f4のp1とp2は両方とも読み出しのみ。 } fn f6 ( p : ptr< private, i32> ) { x = * p ; } fn f7 ( p : ptr< private, i32> ) -> i32{ return x + * p ; } fn f8 () { let a = f6 ( & x ); // 無効。xはグローバル変数として書き込み、パラメータとして読み出される // let b = f7 ( & x ); // 有効。xはパラメータでも変数でも読み出しのみ。 // }
12. 属性
属性はオブジェクトを修飾します。 WGSLは属性を適用するための統一された構文を提供します。 属性はAPIとのインターフェース指定など、様々な目的で使用されます。
一般的に、言語的観点からは、型チェックや意味解析の際に属性は無視できます。 また、属性名はコンテキスト依存名であり、 属性のパラメータの一部もコンテキスト依存名です。
'@'
ident_pattern_token
argument_expression_list ?
| id_attr
属性の説明で明示的に許可されていない限り、属性は同じオブジェクトまたは型に複数回指定してはなりません。
12.1. align
'@'
'align'
'('
expression ','
?
')'
説明 |
構造体メンバーのメモリ上の配置を制約します。
この属性は、囲んでいる構造体型の値がメモリ上でどのように配置されるかに影響します。 構造体自体およびその構成メンバーのバイトアドレスを制約します。 align( n) が S のメンバーに型 T で適用され、
S がアドレス空間 AS の変数の store type になり得る場合、
ここで AS が uniform でないならば、
n は満たさなければならない:
n = k × RequiredAlignOf(T,AS) ただし、k は正の整数。 アラインメントとサイズの規則は相互再帰的です。 しかし、上記の制約は、ネストされた型の必要なアラインメントに依存するため、明確に定義されています。 型には有限のネスト深度があるためです。 |
パラメータ | 必須はconst-expressionであり、解決される型はi32またはu32であること。 必須は正の値であること。 必須は2のべき乗であること。 |
12.2. binding
'@'
'binding'
'('
expression ','
?
')'
説明 | バインドgroup内のリソースのバインディング番号を指定します。 § 13.3.2 リソースインターフェース参照。 |
パラメータ | 必須はconst-expressionであり、解決される型はi32またはu32であること。 必須は0以上であること。 |
12.3. blend_src
'@'
'blend_src'
'('
expression ','
?
')'
説明 |
dual_source_blending機能が有効な場合、
フラグメント出力の一部を指定します。
§ 13.3.1.3 入出力ロケーション参照。
必ず 構造体型の location属性が付与されたメンバーにのみ適用してください。 必ず 数値スカラー型または数値ベクトル型の オブジェクト宣言にのみ適用してください。 絶対に シェーダーステージ入力に含めてはなりません。 絶対に シェーダーステージ出力に含めてはなりませんが、 フラグメントシェーダーステージは例外です。 |
パラメータ | 必ず定数式であり、i32またはu32(値は0 または1 )に解決されなければなりません。
|
12.4. builtin
'@'
'builtin'
'('
builtin_value_name ','
? ')'
説明 | 対象オブジェクトが指定されたトークンにより示される組み込み値であることを指定します。 § 13.3.1.1 組み込み入出力参照。 |
パラメータ | 必ず 組み込み値名トークン(組み込み値用)でなければなりません。 |
12.5. const
'@'
'const'
説明 |
関数が定数関数として使用できることを指定します。
この属性は絶対にユーザー定義関数には付与できません。
必ず関数宣言にのみ適用してください。 注: この属性は、どの組み込み関数が定数式で使用可能かを記述するための記法です。 |
パラメータ | なし |
12.6. diagnostic
'@'
'diagnostic'
diagnostic_control
'('
severity_control_name ','
diagnostic_rule_name ','
? ')'
説明 |
範囲診断フィルターを指定します。§ 2.3
診断参照。
同じ構文要素に複数のdiagnostic属性を指定可能ですが、 必ず異なるトリガールールを指定してください。 |
パラメータ |
1番目のパラメータはseverity_control_nameです。
2番目のパラメータはdiagnostic_rule_nameトークンであり、 トリガールールを指定します。 |
12.7. group
'@'
'group'
'('
expression ','
?
')'
説明 | リソースのバインディンググループを指定します。 § 13.3.2 リソースインターフェース参照。 |
パラメータ | 必ず定数式であり、i32またはu32に解決されなければなりません。 必ず負でない値であること。 |
12.8. id
'@'
'id'
'('
expression ','
?
')'
説明 |
パイプライン上書き可能定数に対して、
数値識別子を別名として指定します。
必ず override宣言のスカラー型にのみ適用してください。 |
パラメータ | 必ず定数式であり、i32またはu32に解決されなければなりません。 必ず負でない値であること。 |
12.9. interpolate
'@'
'interpolate'
'('
interpolate_type_name ','
? ')'
| '@'
'interpolate'
'('
interpolate_type_name ','
interpolate_sampling_name ','
? ')'
説明 | ユーザー定義IOの補間方法を指定します。 この属性はユーザー定義頂点出力およびフラグメント入力でのみ重要です。 § 13.3.1.4 補間参照。 |
パラメータ |
1番目のパラメータは必ず
補間型名トークン(補間型用)でなければなりません。
2番目のパラメータ(存在する場合)は必ず 補間サンプリング名トークン(補間サンプリング用)でなければなりません。 |
12.10. invariant
'@'
'invariant'
説明 |
頂点シェーダーのposition組み込み出力値に適用すると、
結果の計算は異なるプログラムや同じエントリーポイントの異なる呼び出し間で不変となります。
すなわち、2つのエントリーポイントでposition 出力のデータと制御フローが一致する場合、
結果値も必ず一致します。
position の組み込み入力値には効果はありません。
注: この属性はHLSLの |
パラメータ | なし |
12.11. location
'@'
'location'
'('
expression ','
?
')'
説明 |
エントリーポイントのユーザー定義IOの一部を指定します。
§ 13.3.1.3 入出力ロケーション参照。
必ずエントリーポイント関数のパラメータ、エントリーポイント戻り値型、 または構造体型のメンバーにのみ適用してください。 必ず数値スカラー型または数値ベクトル型の オブジェクト宣言にのみ適用してください。 絶対にcompute シェーダーステージ入力には含めてはなりません。 |
パラメータ | 必ず定数式であり、i32またはu32に解決されなければなりません。 必ず負でない値であること。 |
12.12. must_use
'@'
'must_use'
説明 |
この関数への呼び出しは式として使用しなければなりません。
つまり、この関数への呼び出しが関数呼び出し文全体となってはなりません。
注: 多くの関数は値を返し副作用がありません。
そのような関数を関数呼び出し文としてのみ呼び出すのはプログラミング上の欠陥となる場合があります。
この性質を持つ組み込み関数は 注:
|
パラメータ | なし |
12.13. size
'@'
'size'
'('
expression ','
?
')'
説明 |
構造体メンバーに予約されるバイト数を指定します。
この数値は、そのメンバーの型のバイトサイズ以上でなければなりません:
|
パラメータ | 必ず定数式であり、i32またはu32に解決されなければなりません。 必ず正の値であること。 |
12.14. workgroup_size
'@'
'workgroup_size'
'('
expression ','
? ')'
| '@'
'workgroup_size'
'('
expression ','
expression ','
?
')'
| '@'
'workgroup_size'
'('
expression ','
expression ','
expression ','
?
')'
説明 |
コンピュートシェーダーのworkgroup
gridのx, y, z次元を指定します。
1番目のパラメータはx次元を指定します。 2番目のパラメータがあればy次元を指定し、無い場合は1とみなします。 3番目のパラメータがあればz次元を指定し、無い場合は1とみなします。 必須はコンピュートシェーダーのエントリポイント関数にのみ適用されること。 してはならないは他のオブジェクトに適用されてはならないこと。 |
パラメータ |
1個~3個のパラメータを取ります。
各パラメータは定数式またはoverride式でなければなりません。 すべてのパラメータはi32かu32で統一してください。 定数式が0以下であればシェーダー生成エラーです。 パイプライン生成エラーは、 指定パラメータが0以下やWebGPU APIの上限値を超える場合、 またはパラメータ値の積がAPIの上限値を超える場合に発生します (WebGPU § 3.6.2 上限値参照)。 |
12.15. シェーダーステージ属性
以下のシェーダーステージ属性は 特定のシェーダーステージのエントリポイント関数として 関数を指定します。 これらの属性は関数宣言にのみ 適用しなければならず、 一つの関数に対して同時に複数付与することはできません。 パラメータは取りません。
12.15.1. vertex
'@'
'vertex'
vertex
属性は、関数を
バーテックスシェーダーステージの
エントリポイントとして
レンダーパイプラインに宣言します。
12.15.2. fragment
'@'
'fragment'
fragment
属性は、関数を
フラグメントシェーダーステージの
エントリポイントとして
レンダーパイプラインに宣言します。
12.15.3. compute
'@'
'compute'
compute
属性は、関数を
コンピュートシェーダーステージの
エントリポイントとして
コンピュートパイプラインに宣言します。
13. エントリポイント
エントリポイントは、特定のシェーダーステージで処理を行うユーザー定義関数です。
13.1. シェーダーステージ
WebGPUはGPUに対して描画やディスパッチコマンドの形で作業を発行します。 これらのコマンドは、シェーダーステージの入力、出力、および 接続されたリソースのコンテキストでパイプラインを実行します。
パイプラインは、GPU上で実行される作業を、いくつかのステージのシーケンスとして記述します。その中にはプログラム可能なステージも含まれます。 WebGPUでは、描画やディスパッチコマンドを実行する前にパイプラインを作成します。 パイプラインには2種類あります: GPUComputePipeline と GPURenderPipeline。
ディスパッチコマンドは、 GPUComputePipeline を使用して、 コンピュートシェーダーステージ を論理的なグリッド上の多数のポイントで並列性を制御しながら実行し、 バッファーやイメージリソースを読み込み、必要に応じて更新します。
描画コマンドは、 GPURenderPipelineを使って、 他の固定機能ステージの間に2つのプログラム可能なステージを含むマルチステージプロセスを実行します:
-
バーテックスシェーダーステージは、単一の頂点の入力属性を頂点の出力属性へと変換します。
-
固定機能ステージが頂点をグラフィックプリミティブ(例:三角形)へ変換し、ラスタライズしてフラグメントを生成します。
-
フラグメントシェーダーステージは各フラグメントを処理し、フラグメント出力を生成する場合があります。
-
固定機能ステージがフラグメント出力を消費し、カラーバッファや深度・ステンシルバッファなどの外部状態を更新する場合があります。
WebGPU仕様ではパイプラインについてさらに詳しく記述されています。
WGSLは、パイプラインのプログラム可能な部分に対応する3つのシェーダーステージを定義しています:
-
コンピュート
-
バーテックス
-
フラグメント
各シェーダーステージは、それぞれ独自の機能と制約を持ちます(詳細は他の箇所に記載)。
13.2. エントリポイント宣言
エントリポイントを作成するには、シェーダーステージ属性付きのユーザー定義関数を宣言します。
WebGPU APIでパイプラインを構成する際、
エントリポイントの関数名はWebGPUのGPUProgrammableStage
オブジェクトのentryPoint
属性にマッピングされます。
エントリポイントの形式的なパラメータは、そのステージのシェーダーステージ入力を示します。 エントリポイントの戻り値(指定される場合)は、そのステージのシェーダーステージ出力を示します。
各形式的パラメータの型およびエントリポイントの戻り値の型は、以下のいずれかでなければなりません:
構造体型は、ユーザー定義入力同士や、必要に応じて組み込み入力とグループ化するために使用できます。 また、構造体型を戻り値の型として使用することで、ユーザー定義出力同士や、必要に応じて組み込み出力とグループ化できます。
注: bool型はユーザー定義入力/出力には使用できません。 front_facing組み込み値にのみ許可されています。
注: コンピュートエントリポイントは戻り値の型を持ちません。
@vertex fn vert_main () -> @builtin ( position) vec4< f32> { return vec4< f32> ( 0.0 , 0.0 , 0.0 , 1.0 ); } @fragment fn frag_main ( @builtin ( position) coord_in : vec4< f32> ) -> @location ( 0 ) vec4< f32> { return vec4< f32> ( coord_in . x , coord_in . y , 0.0 , 1.0 ); } @compute @workgroup_size ( 1 ) fn comp_main () { }
シェーダーステージ内の関数群は、次の集合の和集合です:
-
そのステージのエントリポイント関数
-
シェーダーステージ内の関数本体から呼び出される関数のターゲット(呼び出しが実際に実行されるかどうかは問わない)
この和集合は安定するまで繰り返し適用されます。 有限回のステップで安定します。
13.2.1. エントリポイントの関数属性
WGSLでは、エントリポイント宣言に適用できる以下の属性が定義されています:
@compute @workgroup_size ( 8 , 4 , 1 ) fn sorter () { } @compute @workgroup_size ( 8u ) fn reverser () { } // パイプラインで上書き可能な定数の使用例 @id ( 42 ) override block_width = 12u ; @compute @workgroup_size ( block_width ) fn shuffler () { } // エラー: workgroup_size は compute シェーダーで指定する必要があります @compute fn bad_shader () { }
13.3. シェーダーインターフェース
シェーダーインターフェースは、シェーダーステージ外部のデータへアクセスするため、 読み書きやシェーダーの設定に用いる パイプライン上書き可能 定数を含むオブジェクト群です。 このインターフェースには以下が含まれます:
-
付加された リソース(以下を含む):
宣言 D がシェーダーによって静的アクセスされるのは次の場合です:
-
識別子が Dに解決され、 関数宣言 内で シェーダーステージ内の関数のいずれかに現れる場合。
ここで、シェーダーのインターフェースを次の構成要素で厳密に定義できます:
-
エントリポイントの仮引数。 これらはシェーダーステージ入力を示します。
-
エントリポイントの戻り値。 これらはシェーダーステージ出力を示します。
-
ユニフォームバッファ、 ストレージバッファ、 テクスチャリソース、 サンプラーリソース変数が 静的アクセスされる場合。
13.3.1. ステージ間の入力・出力インターフェース
シェーダーステージ入力 は、パイプラインの上流からそのステージに渡されるデータです。 各データは 組み込み入力値 か、ユーザー定義入力です。
シェーダーステージ出力 は、パイプラインの下流処理へ渡すためのデータです。 各データは 組み込み出力値 か、ユーザー定義出力です。
IO属性は、 オブジェクトを シェーダーステージ入力や シェーダーステージ出力として定義し、 さらに入力や出力のプロパティを記述します。 IO属性は以下です:
13.3.1.1. 組み込み入力・出力
組み込み入力値 は、システム生成の制御情報を取得するためのものです。 エントリポイントは組み込み入力の重複を含んではなりません。
ステージ S の組み込み入力(名前 X、型 TX)は 以下のいずれかの方法でアクセスされます:
-
パラメータが
builtin(
X)
属性を持ち、型が TX である。 -
パラメータの型が構造体で、そのメンバーのいずれかが
builtin(
X)
属性を持ち、型が TX である。
逆に、エントリポイントのパラメータまたはパラメータのメンバーがbuiltin属性を持つ場合、 対応する組み込み値は必ずそのエントリポイントのシェーダーステージの入力でなければなりません。
組み込み出力値は、 シェーダーが制御情報をパイプラインの後続処理へ渡すためのものです。 エントリポイントは組み込み出力の重複を含んではなりません。
ステージ S の組み込み出力(名前 Y、型 TY)は 以下のいずれかの方法で設定されます:
-
エントリポイントの 戻り値型が
builtin(
Y)
属性を持ち、型が TY である。 -
エントリポイントの 戻り値型が構造体型で、 そのメンバーのいずれかが
builtin(
Y)
属性を持ち、型が TY である。
逆に、エントリポイントの戻り値型または戻り値型のメンバーがbuiltin属性を持つ場合、 対応する組み込み値は必ずそのエントリポイントのシェーダーステージの出力でなければなりません。
注: position 組み込みは、頂点シェーダーの出力であり、フラグメントシェーダーの入力でもあります。
組み込み入力値と組み込み出力値をまとめて、組み込み値と呼びます。
以下の表は利用可能な組み込み値をまとめたものです。 それぞれは 組み込み値名トークンであり、トークンとなります。 詳細は後続セクションで説明します。
名前 | ステージ | 方向 | 型 | 拡張 |
---|---|---|---|---|
vertex_index | vertex | input | u32 | |
instance_index | vertex | input | u32 | |
clip_distances | vertex | output | array<f32, N> (N ≤ 8 )
| clip_distances |
position | vertex | output | vec4<f32> | |
fragment | input | vec4<f32> | ||
front_facing | fragment | input | bool | |
frag_depth | fragment | output | f32 | |
primitive_index | fragment | input | u32 | primitive_index |
sample_index | fragment | input | u32 | |
sample_mask | fragment | input | u32 | |
fragment | output | u32 | ||
local_invocation_id | compute | input | vec3<u32> | |
local_invocation_index | compute | input | u32 | |
global_invocation_id | compute | input | vec3<u32> | |
workgroup_id | compute | input | vec3<u32> | |
num_workgroups | compute | input | vec3<u32> | |
subgroup_invocation_id | compute | input | u32 | subgroups |
fragment | ||||
subgroup_size | compute | input | u32 | subgroups |
fragment |
struct VertexOutput { @builtin ( position) my_pos : vec4< f32> , @builtin ( clip_distances ) my_clip_distances : array< f32, 8 > , } @vertex fn vs_main ( @builtin ( vertex_index) my_index : u32, @builtin ( instance_index) my_inst_index : u32, ) -> VertexOutput {} struct FragmentOutput { @builtin ( frag_depth) depth : f32, @builtin ( sample_mask) mask_out : u32} @fragment fn fs_main ( @builtin ( front_facing) is_front : bool, @builtin ( position) coord : vec4< f32> , @builtin ( sample_index) my_sample_index : u32, @builtin ( sample_mask) mask_in : u32, ) -> FragmentOutput {} @compute @workgroup_size ( 64 ) fn cs_main ( @builtin ( local_invocation_id) local_id : vec3< u32> , @builtin ( local_invocation_index) local_index : u32, @builtin ( global_invocation_id) global_id : vec3< u32> , ) {}
13.3.1.1.1. clip_distances
名前 | clip_distances |
ステージ | vertex |
型 | array<f32, N> |
方向 | 出力 |
説明 |
配列の各値はユーザー定義クリップ面への距離を表します。clip
distanceが0 のとき、その頂点は面上にあり、正の値ならクリップ半空間内、負の値なら半空間外です。clip_distancesの配列サイズは8以下でなければなりません。
詳細は WebGPU § 23.2.4
プリミティブのクリッピング を参照。
|
13.3.1.1.2. frag_depth
名前 | frag_depth |
ステージ | fragment |
型 | f32 |
方向 | 出力 |
説明 |
ビューポートの深度範囲内で更新されたフラグメントの深度値。
詳細は WebGPU § 3.3 座標系 を参照。 |
13.3.1.1.3. front_facing
名前 | front_facing |
ステージ | fragment |
型 | bool |
方向 | 入力 |
説明 | 現在のフラグメントがfront-facingプリミティブ上ならtrue、そうでなければfalseです。 |
13.3.1.1.4. global_invocation_id
名前 | global_invocation_id |
ステージ | compute |
型 | vec3<u32> |
方向 | 入力 |
説明 | 現在の呼び出しのglobal invocation ID、つまりコンピュートシェーダーグリッド内での位置です。global_invocation_idの値は workgroup_id × workgroup_size + local_invocation_idと等しいです。 |
13.3.1.1.5. instance_index
名前 | instance_index |
ステージ | vertex |
型 | u32 |
方向 | 入力 |
説明 |
現在のAPIレベルの描画コマンド内での頂点インスタンス番号。
最初のインスタンスの番号は、描画コマンドの |
13.3.1.1.6. local_invocation_id
名前 | local_invocation_id |
ステージ | compute |
型 | vec3<u32> |
方向 | 入力 |
説明 | 現在の呼び出しのlocal invocation ID、つまりワークグループグリッド内での位置です。 |
13.3.1.1.7. local_invocation_index
名前 | local_invocation_index |
ステージ | compute |
型 | u32 |
方向 | 入力 |
説明 | 現在の呼び出しのlocal invocation index、ワークグループグリッド内での呼び出し位置を線形化した値です。 |
13.3.1.1.8. num_workgroups
名前 | num_workgroups |
ステージ | compute |
型 | vec3<u32> |
方向 | 入力 |
説明 |
ディスパッチサイズ、すなわちvec3<u32>(group_count_x, group_count_y, group_count_z) であり、APIによってコンピュートシェーダーがディスパッチされたときの値です。
|
13.3.1.1.9.
position
名前 | position |
ステージ | vertex |
型 | vec4<f32> |
方向 | 出力 |
説明 |
現在の頂点のクリップ位置(
クリップ空間座標における)。
出力値 (x,y,z,w) は WebGPU の 正規化デバイス座標において (x/w, y/w, z/w) にマッピングされます。 WebGPU § 3.3 座標系 および WebGPU § 23.2.4 プリミティブクリッピング を参照してください。 w 座標がゼロの場合、動的エラーが発生します。 |
名前 | position |
ステージ | fragment |
型 | vec4<f32> |
方向 | 入力 |
説明 |
現在のフラグメントの入力位置。
fp をフラグメントの入力位置、 このとき、概略的には: fp.xy = rp.destination.position 詳細な説明:
詳細は WebGPU § 3.3 座標系 および WebGPU § 23.2.5 ラスタライズ を参照。 |
13.3.1.1.10. primitive_index
名前 | primitive_index |
ステージ | fragment |
型 | u32 |
方向 | 入力 |
説明 | 現在のインスタンスに対して描画操作開始から処理されたプリミティブ数に基づいたプリミティブごとのインデックスです。 0から始まり、各ポイント、ライン、トライアングルプリミティブが処理されるごとに1ずつ増加します。インスタンスごとに0にリセットされます。 プリミティブ再スタート値によるストリッププリミティブの再起動はprimitive_indexに影響しません。このインデックスはプリミティブの全フラグメントで一様です。 |
13.3.1.1.11. sample_index
名前 | sample_index |
ステージ | fragment |
型 | u32 |
方向 | 入力 |
説明 |
現在のフラグメントのサンプルインデックス。値は最低0で、最大sampleCount -1となります。sampleCount は
GPUレンダーパイプラインで指定されたMSAAサンプル
count
です。
この属性が適用され、フラグメントシェーダーの効果がsample_indexの値で変化する場合、フラグメントシェーダーはサンプルごとに1回呼び出されます。
詳細は WebGPU § 10.3 GPURenderPipeline を参照。 |
13.3.1.1.12. sample_mask
名前 | sample_mask |
ステージ | fragment |
型 | u32 |
方向 | 入力 |
説明 |
現在のフラグメントのサンプルカバレッジマスク。このビットマスクには、レンダリング中のプリミティブによってカバーされているサンプルが示されます。
詳細は WebGPU § 23.2.11 サンプルマスキング を参照。 |
名前 | sample_mask |
ステージ | fragment |
型 | u32 |
方向 | 出力 |
説明 |
現在のフラグメントのサンプルカバレッジマスク制御。この変数に最後に書き込まれた値がシェーダー出力マスクとなります。値のゼロビットは対応するカラーバッファのサンプルを捨てます。
詳細は WebGPU § 23.2.11 サンプルマスキング を参照。 |
13.3.1.1.13. vertex_index
名前 | vertex_index |
ステージ | vertex |
型 | u32 |
方向 | 入力 |
説明 |
現在のAPIレベルの描画コマンド内での頂点のインデックス(描画インスタンスに依存しない)。
非インデックス描画の場合、最初の頂点のインデックスは描画コマンドの インデックス付き描画の場合、インデックスは頂点に対するインデックスバッファのエントリ+描画コマンドの |
13.3.1.1.14. workgroup_id
名前 | workgroup_id |
ステージ | compute |
型 | vec3<u32> |
方向 | 入力 |
説明 |
現在の呼び出しのworkgroup
ID、すなわちコンピュートシェーダーグリッド全体の中でのワークグループの位置です。
同じワークグループ内の全呼び出しは同じworkgroup IDを持ちます。 ワークグループIDは(0,0,0)から(group_count_x - 1, group_count_y - 1, group_count_z - 1)まで広がります。 |
13.3.1.1.15. subgroup_invocation_id
名前 | subgroup_invocation_id |
ステージ | compute または fragment |
型 | u32 |
方向 | 入力 |
説明 |
現在の呼び出しのsubgroup invocation IDです。
IDは[0, subgroup_size - 1]の範囲内です。 |
13.3.1.1.16. subgroup_size
名前 | subgroup_size |
ステージ | compute または fragment |
型 | u32 |
方向 | 入力 |
説明 | 現在の呼び出しのサブグループのサイズです。 |
13.3.1.2. ユーザー定義入力・出力
ユーザー定義データはパイプラインの最初の入力として、ステージ間で、またはパイプラインの最終出力として渡すことができます。
各ユーザー定義入力データおよび ユーザー定義出力データは必須です:
-
IOロケーションが割り当てられていること。詳細は§ 13.3.1.3 入出力ロケーションを参照。
computeシェーダーはユーザー定義入力・出力を持ってはなりません。
13.3.1.3. 入出力ロケーション
各入出力ロケーションは最大16バイトの値を格納できます。 型のバイトサイズは§ 14.4.1 アラインメントとサイズのSizeOf列で定義されています。 例えば、4要素の浮動小数点ベクトルは1つのロケーションを占有します。
IOロケーションはlocation属性で指定します。
各ユーザー定義入力および出力は必ず明示的に指定されたIOロケーションを持たなければなりません。 エントリポイントIOの各構造体メンバーは必ず組み込み値(§ 13.3.1.1 組み込み入力・出力参照)か、ロケーションが割り当てられている必要があります。
注:ロケーション番号は入力と出力で別々です: エントリポイントのシェーダーステージ入力用ロケーション番号は、出力用ロケーション番号と競合しません。
注:エントリポイントの出力内でロケーションが重複しないように追加ルールは必要ありません。 出力が構造体の場合、上記の最初のルールによって重複が防止されます。 それ以外の場合、出力はスカラーまたはベクトルであり、単一のロケーションしか割り当てられません。
注:エントリポイントで利用可能なロケーション数はWebGPU APIによって定義されます。
ユーザー定義IOは同じ構造体内で組み込み値と混在できます。例えば、
// 組み込み値とユーザー定義入力の混在 struct MyInputs { @location ( 0 ) x : vec4< f32> , @builtin ( front_facing) y : bool, @location ( 1 ) @interpolate ( flat) z : u32} struct MyOutputs { @builtin ( frag_depth) x : f32, @location ( 0 ) y : vec4< f32> } @fragment fn fragShader ( in1 : MyInputs ) -> MyOutputs { // ... }
struct A { @location ( 0 ) x : f32, // 無効:xとyは同じロケーションを共有できない @location ( 0 ) y : f32} struct B { @location ( 0 ) x : f32} struct C { // 無効:ユーザー定義IOを持つ構造体はネスト不可 b : B } struct D { x : vec4< f32> } @fragment // 無効:構造体型にロケーションは適用不可 fn fragShader1 ( @location ( 0 ) in1 : D ) { // ... } @fragment // 無効:in1とin2は同じロケーションを共有できない fn fragShader2 ( @location ( 0 ) in1 : f32, @location ( 0 ) in2 : f32) { // ... } @fragment // 無効:構造体へのロケーションの適用不可 fn fragShader3 ( @location ( 0 ) in1 : vec4< f32> ) -> @location ( 0 ) D { // ... }
13.3.1.4. 補間
著者はinterpolate属性の利用によってユーザー定義IOデータの補間方法を制御できます。 WGSLは補間のタイプと補間のサンプリングの2つの側面を制御できます。
補間タイプは必ず次のいずれかの補間タイプ名トークンでなければなりません:
- perspective
-
値は遠近法的に正しく補間されます。
- linear
-
値は線形(遠近法補正なし)で補間されます。
- flat
-
値は補間されません。
補間サンプリングは必ず次のいずれかの補間サンプリング名トークンでなければなりません:
- center
-
補間はピクセルの中心で行われます。
- centroid
-
補間は、現在のプリミティブ内でフラグメントがカバーしている全サンプル内にある点で行われます。 この値はプリミティブ内の全サンプルで同じとなります。
- sample
-
補間はサンプルごとに行われます。 この属性が適用されるとフラグメントシェーダーはサンプルごとに1回呼び出されます。
- first
-
値はプリミティブの最初の頂点から提供されます。
- either
-
値はプリミティブの最初または最後の頂点から提供されます。どちらから提供されるかは実装依存です。
スカラーまたはベクトル浮動小数点型のユーザー定義IOについて:
-
補間属性が指定されていない場合、
@interpolate(perspective, center)
が仮定されます。 -
補間属性が補間タイプとともに指定された場合:
スカラーまたはベクトル整数型のユーザー定義頂点出力およびフラグメント入力は必ず補間タイプflat
で指定しなければなりません。
ステージ間インターフェースの検証は、レンダーパイプライン内で 各ユーザー定義フラグメント入力の補間プロパティが同じlocationを割り当てた頂点出力の補間プロパティと一致するかをチェックします。 一致しない場合、パイプライン生成エラーが発生します。
13.3.2. リソースインターフェース
リソースは、シェーダーステージ外部のデータにアクセスするためのオブジェクトであり、 オーバーライド宣言やシェーダーステージ入力・出力ではありません。 リソースはシェーダーの全呼び出しで共有されます。
リソースには4種類あります:
シェーダーのリソースインターフェースは 静的アクセスされるモジュールスコープの リソース変数の集合です。 シェーダーステージの関数によってアクセスされます。
各リソース変数は必ずgroupとbinding属性を持って宣言しなければなりません。 これらはシェーダーステージと組み合わせて、シェーダーのパイプライン上のリソースのバインドアドレスを特定します。 詳細はWebGPU § 8.3 GPUPipelineLayoutを参照。
同じシェーダー内の2つの異なるリソース変数は 必ず groupとbinding値のペアを共有してはなりません。
13.3.3. リソースレイアウトの互換性
WebGPUは、シェーダーのリソースインターフェースが使用しているパイプラインのレイアウトと一致することを要求します。
WGSLのリソースインターフェース内の変数が、互換性のないWebGPU binding member または binding type にバインドされている場合、これはパイプライン作成エラーとなります。互換性は以下の表で定義されています。
WebGPU API 現行標準仕様にてインターフェースの検証要件を参照してください。
13.3.4. バッファバインディングによる実行時サイズ配列の要素数決定
storage buffer
変数に実行時サイズの配列が含まれている場合、その配列の要素数は対応するresource
のサイズから決定されます。
Tをストア型とし、storage buffer変数に対し、 Tが実行時サイズ配列型またはその型を含むものとする。
bufferBindingを get as buffer binding(
resource
) とする。EBSを 実効バッファバインディングサイズとし、 bufferBindingがパイプラインのバインディングアドレスにバインドされた該当storage buffer変数用のものとする。
そして NRuntime、 すなわち実行時サイズ配列の要素数は SizeOf(T) ≤ EBS を満たす最大の整数とする。
詳細として、型RATの実行時サイズ配列に対するNRuntimeは:
truncate((EBBS − array_offset) ÷ array_stride)、ここで
EBBSは、その変数に関連づけられた実効バッファバインディングサイズです。
array_offsetは、ストア型内で実行時サイズ配列のバイトオフセットです。
ストア型がRAT(配列型自体)の場合は0です。
それ以外の場合は、ストア型が構造体であり、その最後のメンバーが実行時サイズ配列です。 この場合、array_offsetはそのメンバーの構造体内バイトオフセットです。
array_strideは配列型のストライド、すなわちStrideOf(RAT)です。
シェーダーはNRuntimeを、arrayLength組み込み関数を使って計算できます。
NRuntimeは対応するバッファバインディングのサイズにより決定され、 これはdraw またはdispatch commandごとに異なる場合があります。
WebGPUの検証規則により、1 ≤ NRuntimeが保証されます。
-
weights
変数はstorage bufferです。 -
そのストア型は実行時サイズ配列型
array<f32>
です。 -
配列オフセットは0です。
-
配列ストライドはStrideOf(array<f32>) = 4です。
次の表は、対応する実効バッファバインディングサイズに基づくweights
変数のNRuntimeの例を示します。
実効バッファバインディングサイズ | weights 変数のNRuntime
| 計算方法 |
---|---|---|
1024 | 256 | truncate( 1024 ÷ 4 ) |
1025 | 256 | truncate( 1025 ÷ 4 ) |
1026 | 256 | truncate( 1026 ÷ 4 ) |
1027 | 256 | truncate( 1027 ÷ 4 ) |
1028 | 257 | truncate( 1028 ÷ 4 ) |
-
lights
変数はstorage bufferです。 -
そのストア型は
LightStorage
です。 -
LightStorage
のpoint
メンバーは、型array<PointLight>
の実行時サイズ配列です。
struct PointLight { // align(16) size(32) position: vec3f, // offset(0) align(16) size(12) // -- implicit member alignment padding -- // offset(12) size(4) color : vec3f, // offset(16) align(16) size(12) // -- implicit struct size padding -- // offset(28) size(4) } struct LightStorage { // align(16) pointCount : u32, // offset(0) align(4) size(4) // -- implicit member alignment padding -- // offset(4) size(12) point : array< PointLight > , // offset(16) align(16) elementsize(32) } @group ( 0 ) @binding ( 1 ) var < storage> lights : LightStorage ;
以下の表は、lights
変数のpoint
メンバーに対するNRuntimeの例を示します。
実効バッファバインディングサイズ | lights 変数のpoint メンバーのNRuntime
| 計算方法 |
---|---|---|
1024 | 31 | truncate( ( 1024 - 16 ) ÷ 32) ) |
1025 | 31 | truncate( ( 1025 - 16 ) ÷ 32) ) |
1039 | 31 | truncate( ( 1039 - 16 ) ÷ 32) ) |
1040 | 32 | truncate( ( 1040 - 16 ) ÷ 32) ) |
14. メモリ
WGSLでは、storable型の値はメモリに格納でき、後で取得することができます。 このセクションでは、メモリの構造と、メモリにアクセスする操作の意味論について説明します。 メモリに格納できる値の型や、メモリアクセスに使用される型については§ 6.4 メモリビューを参照してください。
14.1. メモリロケーション
メモリは一連の個別なメモリロケーションから構成されます。 各メモリロケーションは8ビットのサイズです。 メモリに影響を与える操作は、1つ以上のメモリロケーションの集合とやりとりします。 合成型に対するメモリ操作はパディングされたメモリロケーションをアクセスしません。 そのため、操作によってアクセスされるメモリロケーションの集合は連続していない場合があります。
2つのメモリロケーションの集合は、そのメモリロケーションの集合の交差が空でない場合、オーバーラップします。
14.2. メモリアクセスモード
メモリアクセスは、 メモリロケーションに作用する操作です。
-
読み取りアクセスは、メモリロケーションの内容を観察します。
-
書き込みアクセスは、メモリロケーションの内容を設定します。
1つの操作で、読み取り・書き込み、または両方を行うことができます。
特定のメモリロケーションは、メモリのアクセスモードとして表現される、特定の種類のアクセスのみをサポートする場合があります。
アクセスモード | サポートされるアクセス |
---|---|
read | 読み取りアクセスのみサポート、書き込み不可。 |
write | 書き込みアクセスのみサポート、読み取り不可。 |
read_write | 読み取り・書き込み両方のアクセスをサポート。 |
WGSLは事前宣言された列挙子としてread
、write
、read_write
を持ちます。
14.3. アドレス空間
メモリロケーションはアドレス空間で分割されています。 各アドレス空間は、 可変性・可視性・格納できる値・変数の使用方法など独自の特性を持ちます。 詳細は§ 7 変数と値の宣言を参照してください。
特定のメモリビューのアクセスモードは、しばしば文脈によって決まります:
storageアドレス空間は readおよび read_writeアクセスモードの両方をサポートします。 他のアドレス空間は1つのアクセスモードのみをサポートします。 各アドレス空間のデフォルトアクセスモードは以下の表で説明されています。
アドレス空間 | 呼び出し間の共有 | デフォルトアクセスモード | 備考 |
---|---|---|---|
function | 同じ呼び出しのみ | read_write | |
private | 同じ呼び出しのみ | read_write | |
workgroup | 同じコンピュートシェーダーのワークグループ内の呼び出し | read_write | 最外層の配列の要素数はパイプラインオーバーライド可能定数にできる。 |
uniform | 同じシェーダーステージの呼び出し | read | uniform buffer変数用 |
storage | 同じシェーダーステージの呼び出し | read | storage buffer変数用 |
handle | 同じシェーダーステージの呼び出し | read | samplerおよびtexture変数用 |
WGSLは各アドレス空間に対して事前宣言された列挙子を持ちますが、handle
アドレス空間にはありません。
workgroupアドレス空間内の変数は、必ず静的にアクセスされるように、コンピュートシェーダーステージでのみ使用できます。
storageアドレス空間(storage buffer)の変数は、アクセスモードがreadの場合のみ頂点シェーダーステージから静的にアクセスできます。
ストア型がstorage textureかつ
writeまたはread_writeのアクセスモードの場合は
頂点シェーダーステージから静的アクセスできません。
詳細はWebGPU createBindGroupLayout()
を参照。
注: 各アドレス空間は異なるパフォーマンス特性を持つ場合があります。
-
その他のアドレス空間では、アクセスモードを記述してはなりません。
14.4. メモリレイアウト
WGSLにおける型のレイアウトは、アドレス空間とは独立しています。 しかし厳密に言えば、そのレイアウトはホスト共有可能バッファによってのみ観察できます。 Uniform bufferやstorage buffer変数は、 メモリ上でバイト列として組織された大量データを共有するために使用されます。 バッファはCPUとGPU間、あるいはパイプライン内の異なるシェーダーステージ間、または異なるパイプライン間で共有されます。
バッファデータは再フォーマットや変換なしで共有されるため、バッファの生成者と利用者がメモリレイアウト(バッファ内のバイトがWGSLの型付き値にどのように構成されるかの記述)に合意していない場合、 動的エラーとなります。 これらのバイトは、共通の基準位置からの値のメモリロケーションです。
バッファ変数のストア型は必須で ホスト共有可能であり、以下で説明する完全なメモリレイアウトが必要です。
各バッファ変数は必須で、 uniformまたは storageアドレス空間で宣言されなければなりません。
型のメモリレイアウトは、次の場合にのみ意味を持ちます:
8ビットバイトはホスト共有可能メモリの最も基本的な単位です。 このセクションで定義される用語は、8ビットバイトの数を示します。
以下の記法を用います。ここで Tはホスト共有可能または固定フットプリント型、 Sはホスト共有可能または固定フットプリント構造体型、 Aはホスト共有可能または固定フットプリント配列か実行時サイズ配列です:
-
AlignOf(T) はTのアライメントです。
-
AlignOfMember(S, i)はSの第iメンバーのアライメントです。
-
SizeOf(T)は Tのバイトサイズです。
-
SizeOfMember(S, i)はSの第iメンバーのサイズです。
-
OffsetOfMember(S, i)はSの第iメンバーの開始位置からのオフセットです。
-
StrideOf(A) は要素ストライドであり、 Aの1要素の開始位置から次要素の開始位置までのバイト数です。 これは要素型のサイズを、その型のアライメントに切り上げた値です:
StrideOf(array<E, N>) = roundUp(AlignOf(E), SizeOf(E))
StrideOf(array<E>) = roundUp(AlignOf(E), SizeOf(E))
14.4.1. アライメントとサイズ
各ホスト共有可能または固定フットプリントデータ型 Tにはアライメントとサイズがあります。
アライメントとは、その型の値が メモリ上のどこに配置できるかを制約する整数であり、 型のアライメントは必須で 値の開始メモリロケーションのバイトアドレスを割り切る必要があります。 アライメントは値に対するより効率的なハードウェア命令の利用や、 特定のアドレス空間で要求される厳しいハードウェア要件の充足に役立ちます。(アドレス空間レイアウト制約参照)
注: 各アライメント値は必ず2の冪です。
バイトサイズとは、型や構造体メンバーの値を格納するために ホスト共有可能メモリ内で予約される連続バイト数です。 サイズには型末尾の非アドレス可能なパディングが含まれる場合があります。 そのため、値のロードやストアはサイズより少ないメモリロケーションをアクセスすることがあります。
ホスト共有可能および固定フットプリント型のアライメントとサイズは、以下の表で再帰的に定義されます:
ホスト共有可能または固定フットプリント型 T | AlignOf(T) | SizeOf(T) |
---|---|---|
bool
注参照。 | 4 | 4 |
i32、u32、またはf32 | 4 | 4 |
f16 | 2 | 2 |
atomic<T> | 4 | 4 |
vec2<T>(Tはbool、i32、u32、またはf32) | 8 | 8 |
vec2<f16> | 4 | 4 |
vec3<T>(Tはbool、i32、u32、 またはf32) | 16 | 12 |
vec3<f16> | 8 | 6 |
vec4<T>(Tはbool、i32、u32、 またはf32) | 16 | 16 |
vec4<f16> | 8 | 8 |
matCxR
(列メジャー) (一般形) | AlignOf(vecR) | SizeOf(array<vecR, C>) |
mat2x2<f32> | 8 | 16 |
mat2x2<f16> | 4 | 8 |
mat3x2<f32> | 8 | 24 |
mat3x2<f16> | 4 | 12 |
mat4x2<f32> | 8 | 32 |
mat4x2<f16> | 4 | 16 |
mat2x3<f32> | 16 | 32 |
mat2x3<f16> | 8 | 16 |
mat3x3<f32> | 16 | 48 |
mat3x3<f16> | 8 | 24 |
mat4x3<f32> | 16 | 64 |
mat4x3<f16> | 8 | 32 |
mat2x4<f32> | 16 | 32 |
mat2x4<f16> | 8 | 16 |
mat3x4<f32> | 16 | 48 |
mat3x4<f16> | 8 | 24 |
mat4x4<f32> | 16 | 64 |
mat4x4<f16> | 8 | 32 |
struct S(メンバー M1...MN) | max(AlignOfMember(S,1), ... , AlignOfMember(S,N)) | roundUp(AlignOf(S),
justPastLastMember) ここで justPastLastMember = OffsetOfMember(S,N) + SizeOfMember(S,N) |
array<E,
N> | AlignOf(E) | N × roundUp(AlignOf(E), SizeOf(E)) |
array<E> | AlignOf(E) | NRuntime × roundUp(AlignOf(E),SizeOf(E)) ここで NRuntime はTの実行時決定要素数 |
bool
値が4バイトで4バイトアライメントを占有することを規定することで、
実装はメモリ上で隣接するbool値をデータレースなしでサポートできます。
14.4.2. 構造体メンバーのレイアウト
構造体の内部レイアウトは、そのメンバーのサイズとアライメントから算出されます。 デフォルトでは、メンバーは重複なく順番に、メンバーのアライメント要件を満たしつつ密に配置されます。
このデフォルトの内部レイアウトは、レイアウト属性で上書きできます。属性は次の通りです:
構造体型Sの第iメンバーには、 SizeOfMember(S, i)とAlignOfMember(S, i)で表されるサイズとアライメントがあります。 メンバーのサイズとアライメントは、§ 14.4.4 値の内部レイアウトに記載の通り、 構造体の開始位置から各メンバーのバイトオフセットを計算するために使われます。
SizeOfMember(S, i)は、第iメンバーにsize(k)属性が指定されていればkです。 それ以外の場合は、メンバー型Tに対するSizeOf(T)です。
AlignOfMember(S, i)は、第iメンバーにalign(k)属性が指定されていればkです。 それ以外の場合は、メンバー型Tに対するAlignOf(T)です。
構造体メンバーにsize属性が指定されている場合、 その値はメンバー型のサイズ以上である必要があります:
SizeOfMember(S, i) ≥ SizeOf(T)
ここでTはSの第iメンバーの型です。
構造体の最初のメンバーは必ず開始位置からバイトオフセット0となります:
OffsetOfMember(S, 1) = 0
以降の各メンバーは、型のアライメントを満たし、前メンバーと重複しない最小のオフセット位置に配置されます。 各メンバーインデックスi > 1の場合:
OffsetOfMember(S, i) = roundUp(AlignOfMember(S, i ), OffsetOfMember(S, i-1) + SizeOfMember(S, i-1))
struct A { // align(8) size(24) u : f32, // offset(0) align(4) size(4) v : f32, // offset(4) align(4) size(4) w : vec2< f32> , // offset(8) align(8) size(8) x : f32// offset(16) align(4) size(4) // -- implicit struct size padding -- // offset(20) size(4) } struct B { // align(16) size(160) a : vec2< f32> , // offset(0) align(8) size(8) // -- implicit member alignment padding -- // offset(8) size(8) b : vec3< f32> , // offset(16) align(16) size(12) c : f32, // offset(28) align(4) size(4) d : f32, // offset(32) align(4) size(4) // -- implicit member alignment padding -- // offset(36) size(4) e : A , // offset(40) align(8) size(24) f : vec3< f32> , // offset(64) align(16) size(12) // -- implicit member alignment padding -- // offset(76) size(4) g : array< A , 3 > , // element stride 24 offset(80) align(8) size(72) h : i32// offset(152) align(4) size(4) // -- implicit struct size padding -- // offset(156) size(4) } @group ( 0 ) @binding ( 0 ) var < storage, read_write> storage_buffer : B ;
struct A { // align(8) size(32) u : f32, // offset(0) align(4) size(4) v : f32, // offset(4) align(4) size(4) w : vec2< f32> , // offset(8) align(8) size(8) @size ( 16 ) x : f32// offset(16) align(4) size(16) } struct B { // align(16) size(208) a : vec2< f32> , // offset(0) align(8) size(8) // -- implicit member alignment padding -- // offset(8) size(8) b : vec3< f32> , // offset(16) align(16) size(12) c : f32, // offset(28) align(4) size(4) d : f32, // offset(32) align(4) size(4) // -- implicit member alignment padding -- // offset(36) size(12) @align ( 16 ) e : A , // offset(48) align(16) size(32) f : vec3< f32> , // offset(80) align(16) size(12) // -- implicit member alignment padding -- // offset(92) size(4) g : array< A , 3 > , // element stride 32 offset(96) align(8) size(96) h : i32// offset(192) align(4) size(4) // -- implicit struct size padding -- // offset(196) size(12) } @group ( 0 ) @binding ( 0 ) var < uniform> uniform_buffer : B ;
14.4.3. 配列レイアウト例
// 配列: // - アライメントは4 = AlignOf(f32) // - 要素ストライドは4 = roundUp(AlignOf(f32),SizeOf(f32)) = roundUp(4,4) // - サイズは32 = stride * number_of_elements = 4 * 8 var small_stride : array< f32, 8 > ; // 配列: // - アライメントは16 = AlignOf(vec3<f32>) = 16 // - 要素ストライドは16 = roundUp(AlignOf(vec3<f32>), SizeOf(vec3<f32>)) // = roundUp(16,12) // - サイズは128 = stride * number_of_elements = 16 * 8 var bigger_stride : array< vec3< f32> , 8 > ;
// 配列: // - アライメントは4 = AlignOf(f32) // - 要素ストライドは4 = roundUp(AlignOf(f32),SizeOf(f32)) = 4 // Bがdrawまたはdispatchコマンドのバインディングにおける実効バッファバインディングサイズの場合、要素数は: // N_runtime = floor(B / element stride) = floor(B / 4) @group ( 0 ) @binding ( 0 ) var < storage> weights : array< f32> ; // 配列: // - アライメントは16 = AlignOf(vec3<f32>) = 16 // - 要素ストライドは16 = roundUp(AlignOf(vec3<f32>), SizeOf(vec3<f32>)) // = roundUp(16,12) // Bがdrawまたはdispatchコマンドのバインディングにおける実効バッファバインディングサイズの場合、要素数は: // N_runtime = floor(B / element stride) = floor(B / 16) var < storage> directions : array< vec3< f32>> ;
14.4.4. 値の内部レイアウト
この節では、ホスト共有可能値の内部が、全体値の配置を前提としてバッファのバイト位置にどのように配置されるかを説明します。 これらのレイアウトは値の型や、構造体メンバーのalign属性やsize属性に依存します。
値が配置されるバッファのバイトオフセットは型のアライメント要件を満たす必要があります。型Tの値がバッファオフセットkに配置されるとき、k = c × AlignOf(T)(非負整数c)となります。
データはアドレス空間に関係なく同一に現れます。
注: bool型はホスト共有可能型ではありません。 WGSLではbool値のサイズとアライメントを4バイトと規定しますが、内部レイアウトは規定しません。
型u32またはi32の値Vがホスト共有バッファのバイトオフセットkに配置された場合:
-
バイトkにはVのビット0~7が入ります
-
バイトk+1にはビット8~15が入ります
-
バイトk+2にはビット16~23が入ります
-
バイトk+3にはビット24~31が入ります
注: i32は2の補数表現を使うため、符号ビットはビット位置31です。
64ビット整数レイアウト: WebGPU APIの一部機能は64ビット符号なし整数をバッファに書き込みます。この値Vがホスト共有バッファのバイトオフセットkに現れる場合:
-
バイトkにはビット0~7が入ります
-
バイトk+1にはビット8~15が入ります
-
バイトk+2にはビット16~23が入ります
-
バイトk+3にはビット24~31が入ります
-
バイトk+4にはビット32~39が入ります
-
バイトk+5にはビット40~47が入ります
-
バイトk+6にはビット48~55が入ります
-
バイトk+7にはビット56~63が入ります
型f32の値Vは IEEE-754 binary32形式で表現されます。 1ビットの符号、8ビットの指数部、23ビットの仮数部を持ちます。 Vがホスト共有バッファのバイトオフセットkに配置された場合:
-
バイトkには仮数のビット0~7が入ります。
-
バイトk+1には仮数のビット8~15が入ります。
-
バイトk+2のビット0~6には仮数のビット16~22が入ります。
-
バイトk+2のビット7には指数部のビット0が入ります。
-
バイトk+3のビット0~6には指数部のビット1~7が入ります。
-
バイトk+3のビット7には符号ビットが入ります。
型f16の値Vは IEEE-754 binary16形式で表現されます。 1ビットの符号、5ビットの指数部、10ビットの仮数部を持ちます。 Vがホスト共有バッファのバイトオフセットkに配置された場合:
-
バイトkには仮数のビット0~7が入ります。
-
バイトk+1のビット0~1には仮数のビット8~9が入ります。
-
バイトk+1のビット2~6には指数部のビット0~4が入ります。
-
バイトk+1のビット7には符号ビットが入ります。
注: 上記の規則により、ホスト共有バッファ内の数値はリトルエンディアン形式で格納されます。
atomic型atomic
<T>の値Vがホスト共有バッファに配置される場合、基底型Tの値と同じ内部レイアウトを持ちます。
ベクター型vecN<T>の値Vがホスト共有バッファのバイトオフセットkに配置される場合:
-
V.xはバイトオフセットkに配置されます
-
V.yはバイトオフセットk + SizeOf(T)に配置されます
-
N ≥ 3の場合、V.zはバイトオフセットk + 2 × SizeOf(T)に配置されます
-
N ≥ 4の場合、V.wはバイトオフセットk + 3 × SizeOf(T)に配置されます
行列型matCxR<T>の値Vがホスト共有バッファのバイトオフセットkに配置される場合:
-
Vの列ベクトルiはバイトオフセットk + i × AlignOf(vecR<T>)
配列型Aの値がホスト共有メモリバッファのバイトオフセットkに配置される場合:
-
配列の第i要素はバイトオフセットk + i × StrideOf(A)に配置されます
構造体型Sの値がホスト共有メモリバッファのバイトオフセットkに配置される場合:
-
構造体値の第iメンバーはバイトオフセットk + OffsetOfMember(S,i)に配置されます。 § 14.4.2 構造体メンバーのレイアウト参照。
14.4.5. アドレス空間レイアウト制約
storageおよびuniformアドレス空間は、 異なるバッファレイアウト制約を持ち、本節で説明します。
address spacesのうちuniform以外は すべてstorageアドレス空間と同じ制約を持ちます。
変数が直接または間接的に参照するすべての構造体・配列型は その変数のアドレス空間の制約を必ず守らなければなりません。 アドレス空間制約に違反するとshader-creation errorとなります。
本節では、RequiredAlignOf(S, C)を アドレス空間Cで使用するホスト共有可能型または固定フットプリント型Sのバイトオフセットアライメント要件と定義します。
ホスト共有可能または固定フットプリント型S (SがCに現れると仮定) | RequiredAlignOf(S, C) Cがuniformでない場合 | RequiredAlignOf(S, C) Cがuniformの場合 |
---|---|---|
bool、i32、u32、f32、 またはf16 | AlignOf(S) | AlignOf(S) |
atomic<T> | AlignOf(S) | AlignOf(S) |
vecN<T> | AlignOf(S) | AlignOf(S) |
matCxR<T> | AlignOf(S) | AlignOf(S) |
array<T, N> | AlignOf(S) | roundUp(16, AlignOf(S)) |
array<T> | AlignOf(S) | 該当なし |
struct S | AlignOf(S) | roundUp(16, AlignOf(S)) |
型Tの構造体メンバーは、構造体開始位置から RequiredAlignOf(T, C)の倍数の バイトオフセットで配置される必要があります:
OffsetOfMember(S, i) = k × RequiredAlignOf(T, C)
ここでkは非負整数、構造体Sの第iメンバーの型はT
要素型Tの配列は、アドレス空間Cに対して RequiredAlignOf(T, C)の倍数の 要素ストライドを持つ必要があります:
StrideOf(array<T, N>) = k × RequiredAlignOf(T, C)
StrideOf(array<T>) = k × RequiredAlignOf(T, C)
ここでkは正整数
uniformアドレス空間ではさらに:
-
配列要素は16バイト境界にアライメントされます。 すなわち、StrideOf(array<T,N>) = 16 × k'(正の整数k')
-
構造体メンバー自身が構造体型
S
の場合、そのメンバー開始位置から次メンバー開始位置までのバイト数は roundUp(16, SizeOf(S))以上である必要があります。
注: 以下の例は、uniformバッファのレイアウト要件を満たすために 構造体メンバーにalignやsize属性を使う方法を示します。 特に、GLSLバッファ(std140レイアウト)をWGSLに機械的に変換する際にこれらの技術が使えます。
struct S { x : f32} struct Invalid { a : S , b : f32// invalid: aとbの間のオフセットは4バイトだが、16以上必要 } @group ( 0 ) @binding ( 0 ) var < uniform> invalid : Invalid ; struct Valid { a : S , @align ( 16 ) b : f32// valid: aとbの間のオフセットは16バイト } @group ( 0 ) @binding ( 1 ) var < uniform> valid : Valid ;
struct small_stride { a : array< f32, 8 > // stride 4 } // 無効:ストライドは16の倍数でなければならない @group ( 0 ) @binding ( 0 ) var < uniform> invalid : small_stride ; struct wrapped_f32 { @size ( 16 ) elem : f32} struct big_stride { a : array< wrapped_f32 , 8 > // stride 16 } @group ( 0 ) @binding ( 1 ) var < uniform> valid : big_stride ; // 有効
14.5. メモリモデル
WGSLは一般にVulkanメモリモデルに従います。 この節の残りではWGSLプログラムがVulkanメモリモデルにどのようにマップされるかを説明します。
注: Vulkanメモリモデルは 形式的なAlloyモデルのテキスト版です。
14.5.1. メモリ操作
WGSLでは、読み取りアクセスは Vulkanメモリモデルのメモリ読み取り操作に相当します。 WGSLでは、書き込みアクセスは Vulkanメモリモデルのメモリ書き込み操作に相当します。
読み取りアクセスは、 下記のいずれかを呼び出しが実行したときに発生します:
-
Load Ruleの評価
-
次を除くテクスチャ組み込み関数のいずれか:
-
atomicStore以外のatomic組み込み関数
-
workgroupUniformLoad組み込み関数
書き込みアクセスは、 下記のいずれかを呼び出しが実行したときに発生します:
-
textureStore組み込み関数
-
atomicLoad以外のatomic組み込み関数
-
atomicCompareExchangeWeakは、戻り値の
exchanged
メンバーがtrue
の時だけ書き込みを行います
-
Atomic read-modify-write組み込み関数は 読み取りアクセスと書き込みアクセス両方となる単一のメモリ操作を行います。
読み取り・書き込みアクセスは他の場合に発生しません。 これらはVulkanメモリモデルでメモリ操作と呼ばれます。
メモリ操作は、その操作で使用される特定のメモリビューに関連づけられた メモリロケーション集合のみを正確にアクセスします。 例えば、複数メンバーを持つ構造体のu32をメモリ読み取りする場合、u32メンバーに関連するメモリロケーションのみを読み取ります。
注: ベクターのコンポーネントに対する書き込みアクセスは そのベクターに関連するすべてのメモリロケーションをアクセスする場合があります。
struct S { a : f32, b : u32, c : f32} @group ( 0 ) @binding ( 0 ) var < storage> v : S ; fn foo () { let x = v . b ; // v.aやv.cのメモリロケーションはアクセスしない。 }
14.5.2. メモリモデル参照
モジュールスコープのresource変数は、 一意なメモリモデル参照を groupとbindingのペアごとに形成します。 その他の変数(つまりfunction、 private、 workgroupアドレス空間)は、 変数の存続期間ごとに一意なメモリモデル参照を形成します。
14.5.3. スコープ付き操作
呼び出しがスコープ付き操作を実行する場合、それは1つまたは2つの呼び出し集合に影響します。 これらの集合はメモリスコープと実行スコープです。メモリスコープは、操作によるメモリ内容の更新を観測できる呼び出し集合を指定します。 同期組み込み関数の場合、影響を受ける全てのメモリ操作は、関数より前にプログラム順で行われたものが、 関数より後にプログラム順で行われた操作に可視となります。 実行スコープは、 操作に参加できる呼び出し集合を指定します(§ 15.6 集合操作参照)。
アトミック組み込み関数は、アトミック操作にマッピングされ、そのメモリスコープは次の通りです:
同期組み込み関数は、実行およびメモリスコープがWorkgroup
の制御バリアにマッピングされます。
暗黙的・明示的な微分は、暗黙的にquad実行スコープを持ちます。
注:
生成されたシェーダーでVulkanメモリモデルが有効化されていない場合は、QueueFamily
の代わりにDevice
スコープを使用します。
14.5.4. メモリ意味論
全てのアトミック組み込み関数はRelaxed
メモリ意味論を使用し、ストレージクラス意味論はありません。
注: WGSLのアドレス空間はSPIR-Vのストレージクラスに相当します。
workgroupBarrierはAcquireRelease
メモリ意味論とWorkgroupMemory
意味論を使用します。
storageBarrierはAcquireRelease
メモリ意味論とUniformMemory
意味論を使用します。
textureBarrierはAcquireRelease
メモリ意味論とImageMemory
意味論を使用します。
注:
workgroupBarrier
とstorageBarrier
を組み合わせると、AcquireRelease
順序意味論と両方のWorkgroupMemory
・UniformMemory
メモリ意味論を使用します。
注: いかなるアトミック・同期組み込み関数もMakeAvailable
や
MakeVisible
意味論は使用しません。
14.5.5. privateとnon-private
storage
や
workgroupアドレス空間における非アトミックな読み取りアクセスはすべて
non-privateと見なされ、NonPrivatePointer | MakePointerVisible
メモリオペランドをWorkgroup
スコープで持つ読み取り操作に対応します。
storage
や
workgroupアドレス空間における非アトミックな書き込みアクセスはすべて
non-privateと見なされ、NonPrivatePointer | MakePointerAvailable
メモリオペランドをWorkgroup
スコープで持つ書き込み操作に対応します。
handle
アドレス空間における非アトミックな読み取りアクセスは
non-privateと見なされ、
NonPrivateTexel | MakeTexelVisible
メモリオペランドをWorkgroup
スコープで持つ読み取り操作に対応します。
handle
アドレス空間における非アトミックな書き込みアクセスは
non-privateと見なされ、
NonPrivateTexel | MakeTexelAvailable
メモリオペランドをWorkgroup
スコープで持つ書き込み操作に対応します。
15. 実行
§ 1.1 概要では、シェーダーがどのように呼び出され、invocationに分割されるかを説明しています。 このセクションでは、invocationが個別および集合的にどのように実行されるかに関する追加の制約について説明します。
15.1. Invocation内のプログラム順序
WGSLモジュール内の各ステートメントは、実行中に0回以上実行される可能性があります。 特定のinvocationにおいて、あるステートメントの各実行は一意の 動的ステートメントインスタンス を表します。
ステートメントが式を含む場合、ステートメントの意味論は以下を決定します。
-
式がステートメント実行の一部として評価されるかどうか。
-
ステートメント内の独立した式同士の評価の相対的な順序。
式のネストは、評価を完了するために満たす必要があるデータ依存関係を定義します。
すなわち、ネストされた式は、囲っている式を評価する前に評価されなければなりません。
式のオペランドの評価順序はWGSLでは左から右です。
例えば、foo() + bar()
では、foo()
がbar()
より先に評価されます。
詳しくは§ 8 式を参照してください。
WGSLモジュール内のステートメントは、制御フロー順に実行されます。 詳しくは§ 9 ステートメントおよび§ 11.2 関数呼び出しを参照してください。
15.2. 一様性
集合的操作 (例えば、バリア、微分、または暗黙的に計算された微分に依存するテクスチャ操作など)は、GPU上で同時に動作する複数のinvocation間の協調が必要です。 この操作は、すべてのinvocationが同時に、つまり一様な制御フローで実行された場合に、正しくかつ移植性を持って動作します。
逆に、invocationの厳密な部分集合のみが操作を実行した場合、すなわち非一様な制御フローで実行された場合、 正しくないまたは移植性のない動作が発生します。 非一様な制御依存は、制御フローステートメントの挙動が非一様な値に依存することで生じます。
例えば、異なるinvocationが if、 break-if、 while、 forの条件で異なる値を計算した場合や、 switchのセレクターで異なる値になった場合、 または短絡評価の二項演算子(
&&
や||
)の左オペランドで異なる値になった場合などに非一様な制御依存が生じます。
これらの非一様な値は、多くの場合、静的に一様であると証明されていない特定のソースに遡ることができます。 これらのソースには、以下が含まれますが、これに限定されません:
-
組み込み値のほとんど。ただし、num_workgroupsやworkgroup_idは除く。
-
特定の組み込み関数 (詳しくは§ 15.2.7 関数呼び出しの一様性規則を参照)
正しく移植性のある動作を保証するために、WGSLの実装は現行標準に従い、 静的な一様性解析を行い、各集合的操作が一様な制御フローで実行されることを証明しようとします。 以下のサブセクションで解析について説明します。
一様性失敗は、 現行標準に従い、 一様性解析が特定の集合的操作が一様な制御フローで実行されることを証明できない場合に発生します。
-
微分を計算する組み込み関数で一様性失敗が発生した場合は、 derivative_uniformityの診断が発生します。
-
同期組み込み関数で一様性失敗が発生した場合、 エラーの診断が発生し、 シェーダー生成エラーとなります。
-
サブグループや クアッド組み込み関数で一様性失敗が発生した場合は、 subgroup_uniformity 診断が発生します。
-
診断の発生位置は、その組み込み関数の呼び出し箇所、 またはsubgroupShuffleUp、 subgroupShuffleDown、 subgroupShuffleXorのように一様性が要求されるパラメータの位置です。
-
15.2.1. 用語と概念
以下の定義は参考情報であり、次のサブセクションで解析が計算する内容の直感を与えるものです。 実際には解析こそが、これらの概念やプログラムの妥当性、一様性規則の破れを定義します。
あるinvocationグループについて:
-
特定のスコープ内にいる全てのinvocationが、プログラムのある時点でロックステップで実行しているかのように動作するなら、 その時点は一様な制御フローであると言います。
-
計算シェーダーステージの場合、一様な制御フローのスコープは同じワークグループ内の全invocationです。
-
他のシェーダーステージの場合、一様な制御フローのスコープは同じエントリーポイントで同じ描画コマンド内の全invocationです。
-
-
式が一様な制御フローで実行され、全invocationが同じ値を計算した場合、その値は一様な値であると言います。
-
invocationがローカル変数に対して、変数が生存している全ての時点で同じ値を保持している場合、その変数は一様な変数であると言います。
15.2.2. 一様性解析の概要
以下のサブセクションでは、集合的操作が一様な制御フローでのみ実行されることを静的解析で検証する方法を規定します。
この解析は動的エラーが発生しないことを前提としています。 動的エラーが発生するシェーダーステージは、たとえ一様性解析の結果がどうであれ、すでに移植性がありません。
各関数は2つのことを満たすように解析されます:
-
他の関数を呼び出す際、一様性要件が満たされていること
-
呼び出される際にも一様性要件が満たされていること
この解析の一部として、各関数について、その呼び出し元の解析を助けるためのメタデータを計算します。 つまり、まず呼び出しグラフを構築し、標準ライブラリ以外の関数を呼ばない関数(葉)からエントリーポイントに向かって順に解析します。 こうすることで、関数を解析する時点で、その呼び出し先全てのメタデータがすでに計算されていることが保証されます。 言語仕様上、再帰は禁止されているため、サイクルに陥る心配はありません。
注記:これは言い換えれば、関数を「(間接的に)呼び出し先である」部分順序でトポロジカルソートし、その順序で解析するということです。
さらに、各関数呼び出しについて、解析はその呼び出しが一様な制御フローであることを証明できなかった場合に発生する発生規則の集合を計算・伝播します。 これを呼び出しの潜在的発生セットと呼びます。 このセットの要素は以下のいずれかです:
-
微分計算に依存する関数の場合はderivative_uniformity
-
§ 17.12 サブグループ組み込み関数や§ 17.13 クアッド演算の関数の場合はsubgroup_uniformity
-
フィルターできない一様性要件のための無名の発生規則
-
これは、同期関数に依存する計算シェーダー関数で使われます。
-
15.2.3. 関数の均一性要件の分析
各関数は2つの段階で分析されます。
最初の段階では、関数の構文をたどりながら、以下の小節に記載された規則に従って有向グラフを構築します。 第2段階ではそのグラフを探索し、この関数の呼び出しに関する制約を計算し、 必要に応じて均一性失敗を引き起こすことがあります。
-
プログラムの特定の地点が均一な制御フローで実行されなければならない。
-
式が均一な値でなければならない。
-
変数が均一な変数でなければならない。
-
メモリに保存され、ポインタ経由でロードされる可能性がある値は、均一な値でなければならない。
エッジは、ソースノードに対応するステートメントからターゲットノードに対応するステートメントへの含意として理解できます。
例えば、workgroupBarrier
組み込み関数は均一な制御フロー内でのみ呼び出されなければならないという均一性要件があります。
これを表現するために、RequiredToBeUniform.errorから
workgroupBarrier
の呼び出し箇所に対応するノードへエッジを追加します。
これは、RequiredToBeUniform.errorは命題Trueに対応し、
RequiredToBeUniform.error -> X は Xが真であることを意味します。
逆に、均一性を保証できないもの(例えばスレッドIDを保持する変数など)については、対応するノードからMayBeNonUniformノードへエッジを追加します。 これを理解する方法として、MayBeNonUniformは命題Falseに対応し、X -> MayBeNonUniform は Xが偽であることを意味します。
この解釈の結果、RequiredToBeUniform.errorから到達可能なすべてのノードは、 プログラムが有効であるために均一である必要があるものに対応し、MayBeNonUniformから到達可能なすべてのノードは、 均一性を保証できないものに対応します。 従って、RequiredToBeUniform.errorからMayBeNonUniformへの経路が存在する場合、 均一性違反が発生し、均一性失敗が発生します。
RequiredToBeUniform.warningおよびRequiredToBeUniform.infoノードも同様に使われますが、 これらは警告や情報の診断を出すべき場合を判定するのに使われます:
-
RequiredToBeUniform.warningからMayBeNonUniformへの経路が存在する場合、警告 診断が発生します。
-
RequiredToBeUniform.infoからMayBeNonUniformへの経路が存在する場合、情報 診断が発生します。
§ 2.3 診断で説明されているように、より重大度の高い診断が生成されている場合、低い重大度の診断は破棄されることがあります。
各関数について、2つのタグが計算されます:
-
呼び出し箇所タグ は関数の呼び出し箇所に関する制御フローの均一性要件を記述します。
-
関数タグ は関数の均一性への影響を記述します。
各形式パラメータごとに、1つまたは2つのタグが計算されます:
-
パラメータタグ はパラメータ値の均一性要件を記述します。
-
パラメータ戻り値タグは、パラメータの均一性が関数の戻り値の均一性にどのように影響するかを記述します。
-
ポインタパラメータタグは、パラメータ型が関数アドレス空間へのポインタの場合。 このタグは、関数呼び出しの実行中にパラメータが指すメモリ内の値が非均一になる可能性があるかどうかを記述します。
呼び出し箇所タグ | 説明 |
---|---|
CallSiteRequiredToBeUniform.S, ここで S は次のいずれかの重大度: error, warning, info。 |
関数は均一な制御フローからのみ呼び出さなければなりません。
そうでない場合、重大度Sの診断が発生します。
potential-trigger-setと関連付けられています。 |
CallSiteNoRestriction | 関数は非均一な制御フローから呼び出すことができます。 |
関数タグ | 説明 |
---|---|
ReturnValueMayBeNonUniform | 関数の戻り値は非均一である可能性があります。 |
NoRestriction | 関数は非均一性を導入しません。 |
パラメータタグ | 説明 |
---|---|
ParameterRequiredToBeUniform.S, ここで S は次のいずれかの重大度: error, warning, info。 |
パラメータは均一な値でなければなりません。
パラメータ型がポインタの場合、メモリビュー自体(その内容は必ずしも含まない)は均一でなければなりません。
そうでない場合、重大度Sの診断が発生します。
potential-trigger-setと関連付けられています。 |
ParameterContentsRequiredToBeUniform.S, ここで S は次のいずれかの重大度: error, warning, info。 |
ポインタパラメータが指すメモリに格納された値は均一な値でなければなりません。
そうでない場合、重大度Sの診断が発生します。
potential-trigger-setと関連付けられています。 |
ParameterNoRestriction | パラメータ値には均一性要件はありません。 |
パラメータ戻り値タグ | 説明 |
---|---|
ParameterReturnContentsRequiredToBeUniform | 関数の戻り値が均一な値となるためには、パラメータは均一な値でなければなりません。 パラメータがポインタの場合、ポインタが指すメモリの値も均一でなければなりません。 |
ParameterReturnNoRestriction | パラメータ値には均一性要件はありません。 |
ポインタパラメータタグ | 説明 |
---|---|
PointerParameterMayBeNonUniform | ポインタパラメータが指すメモリの値は、関数呼び出し後に非均一になる可能性があります。 |
PointerParameterNoRestriction | ポインタパラメータが指すメモリの値の均一性は関数呼び出しによって影響を受けません。 |
以下のアルゴリズムは、与えられた関数についてこれらのタグを計算する方法を説明します:
-
次のノードを作成します:
-
RequiredToBeUniform.error, RequiredToBeUniform.warning, および RequiredToBeUniform.info。 これらをまとめてRequiredToBeUniform.Sノードと呼びます。
-
それぞれのノードは、potential-trigger-setと関連付けられており、初期状態では空です。
-
-
MayBeNonUniform
-
CF_startは、関数実行開始時の制御フローの均一性要件を表します。
-
param_i(iは関数の形式パラメータの範囲)
-
関数に戻り値型がある場合は、Value_returnノードを作成します。
-
-
§ 15.2.4 ポインタのデシュガーで説明されているようにポインタをデシュガーします。
-
関数アドレス空間へのポインタ型の形式パラメータごとに、次のノードを作成します:
-
param_i_contents: メモリビューの内容の均一性を表します。
-
Value_return_i_contents: メモリビューの内容の均一性に対する関数の影響を表します。
-
-
-
関数の構文をたどり、関数本体の開始制御フローとしてCF_startを使い、次節(§ 15.2.5 関数スコープ変数値分析、§ 15.2.6 ステートメントの均一性規則、§ 15.2.7 関数呼び出しの均一性規則、§ 15.2.8 式の均一性規則)の規則に従ってノードとエッジをグラフに追加します。
-
この段階で追加されるノードは内部ノードと呼びます。
-
-
以下の初期化を行います:
-
関数タグはNoRestrictionに初期化されます。
-
呼び出し箇所タグはCallSiteNoRestrictionに初期化されます。
-
各param_iについてのパラメータタグはParameterNoRestrictionに初期化されます。
-
各param_iについてのパラメータ戻り値タグはParameterReturnNoRestrictionに初期化されます。
-
各param_iについて、存在する場合はポインタパラメータタグをPointerParameterNoRestrictionに初期化します。
-
-
重大度Sについて、{error, warning, info}の順に、以下を実行します:
-
R.SをRequiredToBeUniform.Sから到達可能な未訪問ノードの集合とする。
-
R.S内の内部ノードを訪問済みとしてマークする。
-
PTSをRequiredToBeUniform.Sに関連付けられたpotential-trigger-setとする。
-
R.SにMayBeNonUniformノードが含まれている場合、均一性失敗を発生させる:
-
各t∈PTSについて、重大度Sおよび発生規則tで診断を発生させる。
-
-
そうでない場合:
-
もしR.SにCF_startが含まれ、呼び出し箇所タグが初期化後に更新されていなければ、 呼び出し箇所タグをCallSiteRequiredToBeUniform.Sに設定し、 そのpotential-trigger-setをPTSに設定します。
-
R.Sに含まれる各param_iについて、 対応するパラメータタグが初期化後に更新されていなければ、 そのタグをParameterRequiredToBeUniform.Sに設定し、 そのpotential-trigger-setをPTSに設定します。
-
R.Sに含まれる各param_i_contentsについて、 対応するパラメータタグが初期化後に更新されていなければ、 そのタグをParameterContentsRequiredToBeUniform.Sに設定し、 そのpotential-trigger-setをPTSに設定します。
-
-
-
すべての内部ノードの訪問済み状態を解除します。
-
Value_returnが存在する場合、 VRをValue_returnから到達可能なノードの集合とします。
-
VRにMayBeNonUniformが含まれていれば、 関数タグをReturnValueMayBeNonUniformに設定します。
-
VRに含まれる各param_iについて、 対応するパラメータ戻り値タグをParameterReturnContentsRequiredToBeUniformに設定します。
-
-
各Value_return_i_contentsノードについて、 VRiをValue_return_i_contentsから到達可能なノードの集合とします。
-
VRiにMayBeNonUniformが含まれていれば、 対応するポインタパラメータタグをPointerParameterMayBeNonUniformに設定します。
-
注: この時点でグラフ全体は破棄可能です。 上記のタグだけが、この関数の呼び出し元を分析するために記憶しておくべき情報です。 しかし、グラフにはより有益な診断を提供するための情報が含まれています。 例えば、ある関数内の値が均一であることを証明できない場合、 それが他の関数で均一性失敗を引き起こす原因となります。 有益な診断では、その非均一な値と、診断の発生箇所となる関数呼び出しも記述されます。
15.2.4. ポインタのデシュガー処理
関数アドレス空間内のポインタ型の各パラメータは、 パラメータをデリファレンスした値と同等の初期値を持つローカル変数宣言としてデシュガーされます。 つまり、関数アドレス空間のポインタはローカル変数宣言のエイリアスとして扱われます。 初期値の代入は、i番目のパラメータに対するparam_i_contentsノードへのエッジを生成します(すなわち、V(e)はparam_i_contentsです)。
各let宣言 Lで、 effective-value-typeがポインタ型の場合、次のようにデシュガーされます:
-
初期化式の各部分式SEをポストオーダーの深さ優先探索で訪問する:
-
(更新された可能性のある)Lの初期化式を記録する。
このデシュガー処理により、ポインタのroot識別子を各使用箇所で直接露出させ、後続の解析を簡素化します。
注: 均一性解析の目的では、型チェックはこのデシュガー前後の両方で行われるものとします。
fn foo ( p : ptr< function, array< f32, 4 >> , i : i32) -> f32{ let p1 = p ; var x = i ; let p2 = & (( * p1 )[ x ]); x = 0 ; * p2 = 5 ; return ( * p1 )[ x ]; } // 均一性解析用に変換した foo の等価バージョン fn foo_for_analysis ( p : ptr< function, array< f32, 4 >> , i : i32) -> f32{ var p_var = * p ; // p 用の変数を導入 let p1 = & p_var ; // p1用に変数を使用 var x = i ; let x_tmp1 = x ; // xの値をキャプチャ let p2 = & ( p_var [ x_tmp1 ]); // p1の初期化式を置換 x = 0 ; * ( & ( p_var [ x_tmp1 ])) = 5 ; // p2の初期化式を置換 return ( * ( & p_var ))[ x ]; // p1の初期化式を置換 }
15.2.5. 関数スコープ変数の値解析
特定の文における関数スコープの変数の値は、 その変数に到達する代入と、場合によっては初期値で解析できます。
代入は、次のいずれかの条件を満たす場合完全代入です:
-
変数のeffective-value-typeがスカラー型である、または
-
変数のeffective-value-typeが複合型であり、 複合型の各要素に値が代入されている場合。
それ以外の代入は部分代入です。
参照型の式で、以下のいずれかの場合完全参照です:
ポインタ型の式で、以下のいずれかの場合完全ポインタです:
注: この解析の目的では、ポインタ型の形式パラメータが完全ポインタとなる場合は不要です。
完全参照および同じく完全ポインタは、 該当する元変数xのすべてのメモリ位置のメモリビューです。
完全参照でない参照は部分参照です。 部分参照は次のいずれかです:
メンバーが1つの構造体型と、その型を格納する変数を考えます:
struct S { member : i32; } fn foo () { var v : S ; }
この場合、v
は完全参照、v.member
は部分参照です。
両者のメモリビューは同じ位置をカバーしますが、v
の格納型はS
、v.s
の格納型はi32
です。
要素数1の配列でも同様の状況が発生します:
fn foo () { var arr : array< i32, 1 > ; }
この場合、arr
は完全参照、arr[0]
は部分参照です。
両者のメモリビューは同じ位置をカバーしますが、arr
の格納型はarray<i32,1>
、arr[0]
の格納型はi32
です。
解析を簡素化するため、いかなる種別の部分参照による代入は、 関連する元変数のすべてのメモリ位置を変更しないものとして扱います。 このため解析は保守的となり、実際より多くのプログラムで均一性失敗が発生しうることになります。
後続の節の均一性規則で、RHSValueとして使われる関数スコープ変数の値は、 RHSValue式評価前の変数値を意味します。 LHSValueとして使われる場合は、式が現れる文の実行後の変数値を意味します。
変数への複数の代入は、制御フロー文や部分代入により、その変数の使用箇所に到達する可能性があります。 解析では、制御フロー文から到達する複数の代入を、 各制御フロー出口に到達する代入集合の和集合として結合します。
次の表は、関数スコープ変数への複数の代入の結合規則を示します。
均一性グラフでは、各結合は結果ノードから値の由来ノードへのエッジです。
任意の変数x
について記述します。以下の記号を使います:
-
Vin(S)は文S実行前の
x
の値です。 -
Vout(S)は文S実行後の
x
の値です。 -
Vout(prev)は現在の文実行前の
x
の値です。 -
Vin(next)は次の文実行前の
x
の値です。 -
V(e)は後続の節で使われる式の値ノードです。
-
V(0)は
x
のeffective-value-typeのゼロ値です。
文 | 結果 | 結果からのエッジ |
---|---|---|
var x; | Vin(next) | V(0) |
var x = e; | Vin(next) |
V(e)
注: これはxへの完全代入です。 |
x = e; | ||
r = e; ここでrは変数xへの完全参照 | ||
r = e; ここでrは変数xへの部分参照 | Vout(S) |
V(e), V(prev)
注: これはxへの部分代入です。 注: 部分代入は前の値も含みます。 代入は格納成分の部分集合のみを書き込むか、書き込まれる値の型が格納型と異なる場合です。 |
s1 s2 ここでNextはs1の動作内にある。 注: s1はしばしばセミコロンで終わります。 | Vin(s2) | Vout(s1) |
if e s1 else s2 ここでNextはs1とs2の両方の動作にある | Vin(next) | Vout(s1), Vout(s2) |
if e s1 else s2 ここでNextはs1の動作にあるがs2にはない | Vin(next) | Vout(s1) |
if e s1 else s2 ここでNextはs2の動作にあるがs1にはない | Vin(next) | Vout(s2) |
loop { s1 continuing { s2 } } | Vin(s1) | Vout(prev), Vout(s2) |
loop { s1 continuing { s2 } } | Vin(s2) | Vout(s1), Vout(si) s1内で動作が{Continue}となりs2に制御を移すすべてのsiに対して |
loop { s1 continuing { s2 } } | Vin(next) | Vout(s2), Vout(si) s1内で動作が{Break}となりnextに制御を移すすべてのsiに対して |
switch e { case _: s1 case _: s2 ... case _: s3 } | Vin(si) | Vout(prev) |
switch e { case _: s1 case _: s2 ... case _: s3 } | Vin(next) | Vout(si), 動作がNextまたはBreakを含むすべてのsiに対して, sj内で動作が{Break}となりnextに制御を移すすべての文についてVout(sj) |
その他のすべての文(関数呼び出しを除く)については、Vin(next)はVout(prev)と同値です。
注: デシュガー方法は文の動作分析と同じです。
15.2.6. 文の均一性規則
文の解析規則は、引数として文自体と、その文の冒頭に対応する制御フローのノード(以下「CF」と記述)を受け取り、以下の両方を返します:
-
その文の出口に対応する制御フローノード
-
グラフに追加する新しいノードとエッジの集合
下表において、(CF1, S) => CF2
は「S の解析を CF1 の制御フローから開始し、必要なグラフ変更を適用し、結果の制御フローを CF2 と命名する」ことを意味します。
同様に (CF1, E) => (CF2, V)
は「CF1 の制御フローから式 E の解析を開始し、必要なグラフ変更を適用し、結果の制御フローノードを CF2、値ノードを V
と命名する」ことを意味します(式の解析については次節参照)。
この式の評価は、左辺でない 代入の式に対して使われ、RHSValueと呼ばれます。
また、左辺の 代入の式に対しても類似の規則があり、それは
LHSValue
であり、LHSValue: (CF, E) => (CF, L)
で表されます。値の均一性に対応するノードではなく、アクセス対象の変数の均一性に対応するノードを算出します。
注: LHSValue には インクリメントや デクリメント 文中の式も含まれます。
注: RHSValue には 右辺の 代入文に含まれる式や、代入・インクリメント・デクリメント文に属さない式が含まれます。
複数のエッジを作成する必要がある場合、X -> {Y, Z}
は X -> Y, X -> Z
の省略記法です。
文 | 新しいノード | 再帰的解析 | 結果の制御フローノード | 新しいエッジ |
---|---|---|---|---|
空文 | CF | |||
{s} | (CF, s) => CF' | CF' | ||
s1 s2, 挙動に Next を含む場合 注: s1 はしばしばセミコロンで終わる。 | (CF, s1) => CF1 (CF1, s2) => CF2 | CF2 | ||
s1 s2, 挙動に Next を含まない場合 注: s1 はしばしばセミコロンで終わる。 |
(CF, s1) => CF1 注: s2 は静的に到達不能であり、再帰的解析しない。 s2 は均一性解析に寄与しない。 | CF1 | ||
if e s1 else s2 挙動 {Next} の場合 | (CF, e) => (CF', V) (V, s1) => CF1 (V, s2) => CF2 | CF | ||
if e s1 else s2 その他の挙動 | CFend | CFend | CFend -> {CF1, CF2} | |
loop {s1 continuing {s2}} 挙動 {Next} の場合 | CF' | (CF', s1) => CF1 (CF1, s2) => CF2 | CF | CF' -> {CF2, CF} |
loop {s1 continuing {s2}} その他の挙動 | CF' | |||
loop {s1} 挙動 {Next} の場合 | CF' | (CF', s1) => CF1 | CF | CF' -> {CF1, CF} |
loop {s1} その他の挙動 | CF' | |||
switch e case _: s_1 .. case _: s_n 挙動 {Next} の場合 | (CF, e) => (CF', V) (V, s_1) => CF_1 ... (V, s_n) => CF_n | CF | ||
switch e case _: s_1 .. case _: s_n その他の挙動 | CFend | CFend | CFend -> {CF_1, ..., CF_n} | |
var x: T; | CF | |||
break; | ||||
continue; | ||||
break if e; | CFend | (CF, e) => (CF', V) | CFend |
CFend -> {CF', V}
注: CFend から V
へのエッジは、条件値が非均一な場合、この |
return; | CF | 各 function アドレス空間ポインタ引数 i について、 Value_return_i_contents -> Vin(prev)(§ 15.2.5 関数スコープ変数値解析参照) | ||
return e; | (CF, e) => (CF', V) | CF' |
Value_return ->
V
各 function アドレス空間ポインタ引数 i について、 Value_return_i_contents -> Vin(prev)(§ 15.2.5 関数スコープ変数値解析参照) | |
e1 = e2; | LHSValue:
(CF, e1) => (CF1, LV) (CF1, e2) => (CF2, RV) | CF2 |
LV -> RV
注: LV は 値解析の結果値です。 | |
_ = e | (CF, e) => (CF', V) | CF' | ||
let x = e; | (CF, e) => (CF', V) | CF' | ||
var x = e; | (CF, e) => (CF', V) | CF' | ||
f() 引数なし関数呼び出し文 | 関数呼び出し解析を実行: (CF, f()) => (CF_after, Result) | CF_after | ||
f( e1,...,eN) 引数あり関数呼び出し文 | 関数呼び出し解析を実行: (CF, f(e1,...,eN)) => (CF_after, Result) | CF_after |
この解析の目的のため:
-
for
ループはデシュガー処理される(§ 9.4.4 For Statement参照) -
while
ループはデシュガー処理される(§ 9.4.5 While Statement参照) -
loop {s}
はloop {s continuing {}}
として扱われる -
else
分岐のないif
文は空の else 分岐(else {}
)があるものとして扱う -
else if
分岐を持つif
文は、入れ子の単純なif/else
文として扱う -
switch_clause が
default
で始まる場合は、switch_clause がcase _:
で始まる場合と同様に動作する
パフォーマンスを最大限に高めるため、実装では 非均一な制御フロー の数を減らす工夫がよく行われます。 ただし、呼び出しが均一とみなせるポイントは、様々な要因によって異なります。WGSLの静的解析では、挙動が {Next} である場合、if、switch、および loop 文の末尾で均一な制御フローに戻ると保守的に仮定します。 このことは、前表において結果の制御フローノードが入力ノードと同じになることで表現されています。
15.2.7. 関数呼び出しの均一性規則
最も複雑な規則は関数呼び出しに関するものです:
-
各引数について、対応する式規則を適用し、前の引数の出口の制御フロー(最初の引数の場合は関数呼び出しの開始時の制御フロー)を使う。対応する値ノードを arg_i、対応する制御フローノードを CF_i と名付ける。
-
新しいノードを2つ作成し、それぞれ Result および CF_after と名付ける。
-
関数の call site tag が CallSiteRequiredToBeUniform.S の場合:
-
RequiredToBeUniform.S から最後の CF_i へエッジを追加する。
-
call site tag の potential-trigger-set のメンバーを RequiredToBeUniform.S に関連付けられた potential-trigger-set に追加する。
-
-
関数の function tag が ReturnValueMayBeNonUniform の場合、Result から MayBeNonUniform へエッジを追加する。
-
各引数 i について:
-
対応する parameter tag が ParameterRequiredToBeUniform.S の場合:
-
RequiredToBeUniform.S から arg_i へエッジを追加する。
-
parameter tag の potential-trigger-set のメンバーを RequiredToBeUniform.S の potential-trigger-set に追加する。
-
-
parameter return tag が ParameterReturnContentsRequiredToBeUniform の場合、 Result から arg_i へエッジを追加する。
-
対応するパラメータが pointer parameter tag で PointerParameterMayBeNonUniform の場合、Vout(call) から MayBeNonUniform へエッジを追加する。
-
パラメータが function アドレス空間のポインタなら、Vout(call) から事前に記録された到達可能なパラメータに対応する各 arg_i へエッジを追加する。
-
parameter tag が ParameterContentsRequiredToBeUniform.S の場合、 RequiredToBeUniform.S から Vout(call) へエッジを追加する。
-
-
注: Vout(call)の定義については§ 15.2.5 関数スコープ変数値分析を参照。
ほとんどの組み込み関数は以下のタグを持ちます:
-
各パラメータについて:
例外リストは以下の通りです:
-
§ 17.11 同期組み込み関数の関数呼び出し:
-
呼び出し箇所タグはCallSiteRequiredToBeUniform.errorであり、potential-trigger-setは無名の発生規則を含みます。
-
注: 発生規則には名前がなく、フィルタできません。
-
-
さらにworkgroupUniformLoad呼び出しの場合は、パラメータ
p
のパラメータタグがParameterRequiredToBeUniform.errorであり、potential-trigger-setは無名の発生規則を含みます。
-
§ 17.6 微分組み込み関数、 § 17.7.8 textureSample、 § 17.7.9 textureSampleBias、 § 17.7.10 textureSampleCompareの関数呼び出し:
-
呼び出し箇所タグは次の通り:
-
DFを呼び出し箇所位置および発生規則derivative_uniformityの最も近い診断フィルタとする。
-
もしDFが存在すれば、SをDFの新しい重大度パラメータとする。
-
もしSがoffなら、呼び出し箇所タグはCallSiteNoRestriction。
-
それ以外の場合、呼び出し箇所タグはCallSiteRequiredToBeUniform.Sであり、potential-trigger-setはderivative_uniformity要素を含みます。
-
-
もしDFが存在しない場合、呼び出し箇所タグはCallSiteRequiredToBeUniform.errorであり、potential-trigger-setはderivative_uniformity要素を含みます。
-
-
§ 17.7.4 textureLoadの関数呼び出し:
-
関数タグは以下の通り:
-
引数
t
がread-write storage textureの場合はReturnValueMayBeNonUniform -
それ以外の場合はNoRestriction
-
-
§ 17.12 サブグループ組み込み関数や§ 17.13 クアッド演算の関数呼び出し:
-
DFを呼び出し箇所位置および発生規則subgroup_uniformityの最も近い診断フィルタとする。
-
呼び出し箇所タグは以下の通り:
-
もしDFが存在すれば、SをDFの新しい重大度パラメータとする。
-
もしSがoffなら、呼び出し箇所タグはCallSiteNoRestriction。
-
それ以外の場合、呼び出し箇所タグはCallSiteRequiredToBeUniform.Sであり、potential-trigger-setはsubgroup_uniformity要素を含みます。
-
-
もしDFが存在しない場合、呼び出し箇所タグはCallSiteRequiredToBeUniform.errorであり、potential-trigger-setはsubgroup_uniformity要素を含みます。
-
-
さらにsubgroupShuffleUpまたはsubgroupShuffleDown呼び出しの場合は、パラメータ
delta
のパラメータタグは以下の通り:-
もしDFが存在すれば、SをDFの新しい重大度パラメータとする。
-
もしSがoffなら、パラメータタグはNoRestriction。
-
それ以外の場合、パラメータタグはParameterRequiredToBeUniform.Sであり、potential-trigger-setはsubgroup_uniformity要素を含みます。
-
-
もしDFが存在しない場合、パラメータタグはParameterRequiredToBeUniform.errorであり、potential-trigger-setはsubgroup_uniformity要素を含みます。
-
-
さらにsubgroupShuffleXor呼び出しの場合は、パラメータ
mask
のパラメータタグは以下の通り:-
もしDFが存在すれば、SをDFの新しい重大度パラメータとする。
-
もしSがoffなら、パラメータタグはNoRestriction。
-
それ以外の場合、パラメータタグはParameterRequiredToBeUniform.Sであり、potential-trigger-setはsubgroup_uniformity要素を含みます。
-
-
もしDFが存在しない場合、パラメータタグはParameterRequiredToBeUniform.errorであり、potential-trigger-setはsubgroup_uniformity要素を含みます。
-
注: WGSLの実装では、関数呼び出し前の制御フローが均一であれば、関数呼び出し後も均一であることが保証されます。
15.2.8. 式の均一性規則
式の分析規則は、式自体と、その開始時の制御フローに対応するノード(以下「CF」と記載)を引数として受け取り、以下を返します:
-
式の終了時の制御フローに対応するノード
-
式の値に対応するノード
-
グラフに追加する新しいノードとエッジの集合
式 | 新しいノード | 再帰的解析 | 結果の制御フローノード・値ノード | 新しいエッジ |
---|---|---|---|---|
e1 || e2 | (CF, e1) => (CF1, V1) (V1, e2) => (CF2, V2) | CF, V2 | ||
e1 && e2 | ||||
リテラル | CF, CF | |||
identifier resolving to 関数スコープ変数 "x" で、 識別子が root identifier として memory view 式 MVE に現れ、load rule が MVE に対して 型チェック中に呼ばれる場合 | Result | X はこの式を含む文の入力時点での "x" の値に対応するノード | CF, Result |
Result -> {CF, X}
注: X は "x" の
Vout(prev) と同等 |
identifier resolving to 関数スコープ変数 "x" で、 "x" がデシュガーされたポインタパラメータ i であり、 識別子が root identifier として memory view 式 MVE に現れ、load rule が MVE に対して 型チェック中に呼ばれない場合 | CF, param_i | |||
identifier resolving to 関数スコープ変数 "x" で、 識別子が root identifier として memory view 式 MVE に現れ、load rule が MVE に対して 型チェック中に呼ばれない場合 | CF, CF | |||
identifier resolving to const-declaration、override-declaration、 let-declaration、または非組み込みの 形式パラメータで 非ポインタ型 "x" | Result | X は "x" に対応するノード | CF, Result | Result -> {CF, X} |
identifier resolving to 形式パラメータで ポインタ型かつ storage、workgroup、 private アドレス空間で 非read-only アクセスモードかつ 識別子が root identifier として memory view 式 MVE に現れ、 load rule が MVE に対して 型チェック中に呼ばれる場合 | CF, MayBeNonUniform | |||
identifier resolving to 形式パラメータで ポインタ型かつ storage、workgroup、 private アドレス空間で 非read-only アクセスモードかつ 識別子が root identifier として memory view 式 MVE に現れ、 load rule が MVE に対して 型チェック中に呼ばれない場合 | CF, CF | |||
identifier resolving to 形式パラメータで ポインタ型かつ function 以外の アドレス空間で read-only アクセスモードの場合 | CF, CF | |||
identifier resolving to 均一な組み込み値 "x" | CF, CF | |||
identifier resolving to 非均一な組み込み値 "x" | CF, MayBeNonUniform | |||
identifier resolving to 読み取り専用なモジュールスコープ変数 "x" | CF, CF | |||
identifier resolving to 非読み取り専用なモジュールスコープ 変数 "x" で、識別子が root identifier として memory view 式 MVE に現れ、load rule が MVE に対して 型チェック中に呼ばれる場合 | CF, MayBeNonUniform | |||
identifier resolving to 非読み取り専用なモジュールスコープ 変数 "x" で、識別子が root identifier として memory view 式 MVE に現れ、load rule が MVE に対して 型チェック中に呼ばれない場合 | CF,CF | |||
( e ) | (CF, e) => (CF', V) | CF', V | ||
op e, op が単項演算子の場合 | ||||
e.field | ||||
e1 op e2, op が短絡しない二項演算子の場合 | Result | (CF, e1) => (CF1, V1) (CF1, e2) => (CF2, V2) | CF2, Result | Result -> {V1, V2} |
e1[e2] | ||||
f() 引数なし関数呼び出し式 | 関数呼び出し解析を実行: (CF, f()) => (CF_after, Result) | CF_after, Result | ||
f( e1,...,eN) 引数あり関数呼び出し式 | 関数呼び出し解析を実行: (CF, f(e1,...,eN)) => (CF_after, Result) | CF_after, Result |
以下の組込み入力変数は均一とみなされます:
その他のもの(組込み値参照)は非均一とみなされます。
注: 著者は、均一な組込み値とその他の非均一な入力をまとめて扱うことを避けるべきです。分析ではコンポーネント型の複合型の各要素を個別には分析しません。
式 | 新ノード | 再帰分析 | 結果の制御フローノード・変数ノード | 新しいエッジ |
---|---|---|---|---|
identifier function-scope変数"x"に解決 | Result | Xはこの式を含む文の出力時の"x"の値に対応するノード | CF, Result |
Result -> {CF, X}
注:
Xは"x"に対するVin(next)と同値 |
identifier const宣言、override宣言、let宣言、形式パラメータ"x"に解決 | Xは"x"に対応するノード | CF, X | ||
identifier モジュールスコープ変数"x"に解決 | CF, MayBeNonUniform | |||
e.field | LHSValue: (CF, e) => (CF1, L1) | CF1, L1 | ||
*e | ||||
&e | ||||
e1[e2] | LHSValue:
(CF, e1) => (CF1, L1) (CF1, e2) => (CF2, V2) | CF2, L1 | L1 -> V2 |
15.2.9. 全ての制御フローポイントの均一性を注釈する
この小節全体は規範的ではありません。
もし実装者が、シェーダー全体の制御フローの各ポイントについて、それが均一かどうか(つまり均一性を要求する関数をそこから呼び出しても有効かどうか)を開発者に診断モードとして表示したい場合は、以下を推奨します:
-
前節で記述された(必須かつ規範的な)分析を実施し、各関数についてグラフを保持する。
-
それら全てのグラフのエッジを反転する。
-
各関数について、エントリーポイントから始め、必ず呼び出し元を全て訪問してからその関数を訪問する:
-
少なくとも一つの呼び出し元で非均一であった引数には、MayBeNonUniformからエッジを追加する。
-
少なくとも一つの呼び出し元で非均一な制御フローで呼び出された場合は、MayBeNonUniformからCF_startへエッジを追加する。
-
MayBeNonUniformから到達可能なノードを調べる。訪問された各ノードは、均一性解析によって均一性が証明できなかった式や制御フローポイントである。
-
この到達可能性解析で訪問されなかったノードは、解析によって均一性が証明できる(つまり、そこで導関数等の関数呼び出しが安全である)ことになります。
注: コール時にどのエッジをグラフに追加すべきかを把握するため、ボトムアップ解析も依然として必要です。
15.2.10. 例
次の例で使用されるグラフは、ノードに対して以下の表記法を用います:
-
矩形は値ノードを表します。
-
角が丸い矩形は制御フローノードを表します。
15.2.10.1.
無効な textureSample
関数呼び出し
この例はtextureSample組込み関数呼び出しの無効な使用例を示します。
この関数呼び出しは、条件が非均一値(組込み値position
)に依存するif文の中で行われています。
無効な依存チェーンは赤色で強調されています。
@group ( 0 ) @binding ( 0 ) var t : texture_2d< f32> ; @group ( 0 ) @binding ( 1 ) var s : sampler; @fragment fn main ( @builtin ( position) pos : vec4< f32> ) { if ( pos . x < 0.5 ) { // 無効な textureSample 関数呼び出し。 _ = textureSample ( t , s , pos . xy ); } }
この例はまた、if文の後の制御フローの均一性がif文の前と同じであることも示しています(CF_returnがCF_startに接続されている)。
つまり、エントリーポイントの開始時に制御フローが均一であることが保証されているため、if文の後も再び制御フローが均一になります。
textureSample
関数呼び出しがif文の外に移動されていれば、このプログラムは有効だったでしょう。
また、if文の条件が均一値(例えば各呼び出しが同じ値をuniform bufferから読み取る等)であれば、このプログラムも有効です。
15.2.10.2. 関数スコープ変数の均一性
この例は、関数スコープ変数の値に依存するbarrier関数呼び出しの有効・無効両方のケースを示します。
workgroupBarrier
は、x
の値が可変なモジュールスコープ変数a
から導出されるため無効です。
storageBarrier
は、x
の値が不変なモジュールスコープ変数b
から導出されるため有効です。
この例は値分析が関数スコープ変数のライフタイム内の異なる均一性期間を分離できることを強調しています。
また、最初のif文の終了後、制御フローが再び均一になることも明示されています。
これは、グラフのその部分が2つ目のif文とは独立しているため分かります。
@group ( 0 ) @binding ( 0 ) var < storage, read_write> a : i32; @group ( 0 ) @binding ( 1 ) var < uniform> b : i32; @compute @workgroup_size ( 16 , 1 , 1 ) fn main () { var x : i32; x = a ; if x > 0 { // 無効な barrier 関数呼び出し。 workgroupBarrier (); } x = b ; if x < 0 { // 有効な barrier 関数呼び出し。 storageBarrier (); } }
注: サブグラフは理解しやすくするために例として含まれています。
15.2.10.3. 複合値分析の制限
均一性分析の制約の一つは、コンポーネントを合成値の個々で独立して追跡しないことです。 つまり、非均一なコンポーネント値がある場合、 分析は合成値全体を非均一として扱います。 この例はこの問題と、シェーダー作者がこの制約を回避するために利用できる潜在的な方法を示しています。
struct Inputs { // workgroup_idは均一な組込み値。 @builtin ( workgroup_id) wgid : vec3< u32> , // local_invocation_indexは非均一な組込み値。 @builtin ( local_invocation_index) lid : u32} @compute @workgroup_size ( 16 , 1 , 1 ) fn main ( inputs : Inputs ) { // この比較は常に均一ですが、分析では判定できません。 if inputs . wgid . x == 1 { workgroupBarrier (); } }
この分析の制限を回避する最も簡単な方法は、複合値を分割し、均一であることが分かっている値と非均一であることが分かっている値を別々にすることです。 下記の代替WGSLでは、2つの組込み値を別々のパラメータに分割することで均一性分析を通過できます。 これは、グラフ内にRequiredToBeUniform.SからMayBeNonUniformへの経路がないことから分かります。
@compute @workgroup_size ( 16 , 1 , 1 ) fn main ( @builtin ( workgroup_id) wgid : vec3< u32> , @builtin ( local_invocation_index) lid : u32) { // 均一性分析はこの比較が常に均一であることを正しく判定できます。 if wgid . x == 1 { // 有効な barrier 関数呼び出し。 workgroupBarrier (); } }
15.2.10.4. ループ内の均一性
この例では、ループ内で無効なworkgroupBarrier
関数呼び出しが行われています。
非均一な組込み値local_invocation_index
が最終的な原因であり、これはbarrierの後に登場していますが、その事実に関わらず無効です。
これは、後のイテレーションでワークグループ内の一部の呼び出しがループを早期に抜ける一方、他の呼び出しがbarrierを実行しようとするために起こります。
解析では、ループ本体の開始時の制御(CF_loop_body)がループ本体終了時の制御(CF_after_if)に依存するエッジとして、イテレーション間の依存関係がモデル化されています。
@compute @workgroup_size ( 16 , 1 , 1 ) fn main ( @builtin ( local_invocation_index) lid : u32) { for ( var i = 0u ; i < 10 ; i ++ ) { workgroupBarrier (); if ( lid + i ) > 7 { break ; } } }
15.2.10.5. ユーザー定義関数呼び出し
この例は最初の例を修正したもので、ユーザー定義の関数呼び出しを使用します。
解析では、scale
の両方のパラメータにparameter return tagとして
ParameterReturnContentsRequiredToBeUniformが設定されます。
これにより、main
内でscale
関数呼び出しの戻り値とposition
組込み値の間にパスができます。
このパスは、RequiredToBeUniform.SからMayBeNonUniformへの全体の無効なパスの部分パスです。
fn scale ( in1 : f32, in2 : f32) -> f32{ let v = in1 / in2 ; return v ; } @group ( 0 ) @binding ( 0 ) var t : texture_2d< f32> ; @group ( 0 ) @binding ( 1 ) var s : sampler; @fragment fn main ( @builtin ( position) pos : vec4< f32> ) { let tmp = scale ( pos . x , 0.5 ); if tmp > 1.0 { _ = textureSample ( t , s , pos . xy ); } }
注: サブグラフは理解しやすくするために例として含まれています。
15.3. コンピュートシェーダーとワークグループ
ワークグループは、 コンピュートシェーダーステージのエントリーポイントを同時に実行する一連の呼び出しであり、 workgroupアドレス空間のシェーダー変数へのアクセスを共有します。
コンピュートシェーダーのワークグループグリッドは、 整数座標(i,j,k)の点の集合で、以下を満たします:
-
0 ≤ i < workgroup_size_x
-
0 ≤ j < workgroup_size_y
-
0 ≤ k < workgroup_size_z
ここで、(workgroup_size_x, workgroup_size_y, workgroup_size_z)は エントリーポイントのworkgroup_size属性で指定された値です。
ワークグループグリッドの各点に対して、ワークグループには正確に1つの呼び出しが存在します。
呼び出しのローカル呼び出しIDは、 対応するワークグループグリッド点の座標三つ組(i, j, k)です。
呼び出しがローカル呼び出しIDを持つ場合、 そのローカル呼び出しインデックスは
i + (j * workgroup_size_x) + (k * workgroup_size_x * workgroup_size_y)
ワークグループにW個の呼び出しがある場合、 各呼び出しIはユニークなローカル呼び出しインデックスL(I)を持ち、 0 ≤ L(I) < Wを満たし、この範囲全体がカバーされます。
コンピュートシェーダーは、WebGPU実装がディスパッチコマンドをキューから取り出し、指定された作業をGPU上で開始した時に実行が始まります。 ディスパッチコマンドはディスパッチサイズを指定します。 これは整数三つ組(group_count_x, group_count_y, group_count_z)であり、 実行されるワークグループ数を示します(詳細は後述)。
特定のディスパッチのコンピュートシェーダーグリッドは、 整数座標(CSi,CSj,CSk)の点の集合で、以下を満たします:
-
0 ≤ CSi < workgroup_size_x × group_count_x
-
0 ≤ CSj < workgroup_size_y × group_count_y
-
0 ≤ CSk < workgroup_size_z × group_count_z
ここでworkgroup_size_x、 workgroup_size_y、 workgroup_size_zは、上記のコンピュートシェーダーエントリーポイントで指定された値です。
コンピュートシェーダーのディスパッチで行う作業は、コンピュートシェーダーグリッドの各点に対してエントリーポイントを正確に1回呼び出すことです。
呼び出しのグローバル呼び出しIDは、 対応するコンピュートシェーダーグリッド点の座標三つ組です。
呼び出しはワークグループに編成されており、各呼び出しの グローバル呼び出しID (CSi, CSj, CSk)は 以下で識別される単一のワークグループにマップされます:ワークグループID:
( ⌊ CSi ÷ workgroup_size_x ⌋, ⌊ CSj ÷ workgroup_size_y ⌋, ⌊ CSk ÷ workgroup_size_z ⌋)
さらに、そのワークグループ内の単一の呼び出しは、ローカル呼び出しIDで識別されます:
( CSi mod workgroup_size_x , CSj mod workgroup_size_y , CSk mod workgroup_size_z )
注: ワークグループIDは(0,0,0)から(group_count_x - 1, group_count_y - 1, group_count_z - 1)までの範囲です。
WebGPUでは以下について何も保証しません:
-
異なるワークグループの呼び出しが同時に実行されるかどうか。 つまり、同時に複数のワークグループが実行されると仮定してはなりません。
-
あるワークグループの呼び出しが実行を開始すると、他のワークグループが実行をブロックされるかどうか。 つまり、同時に1つのワークグループしか実行されないと仮定してはなりません。 ワークグループが実行中でも、実装は他のワークグループや他のキュー内の未ブロック作業を同時に実行することがあります。
-
ある特定のワークグループの呼び出しが他のワークグループの呼び出しよりも先に実行を開始するかどうか。 つまり、ワークグループが特定の順序で起動されると仮定してはなりません。
15.4. フラグメントシェーダーとヘルパー呼び出し
フラグメントシェーダーステージの呼び出しは、XおよびY次元で隣接する位置を持つ2x2グリッドに分割されます。 これらのグリッドはクアッドと呼ばれます。 クアッドは一部の集団演算で協調することができます(§ 15.6.2 微分参照)。 呼び出しのクアッド呼び出しIDは、クアッド内で一意のIDであり、以下のとおりです:
-
ID 0は左上の呼び出し。
-
ID 1は右上の呼び出し。
-
ID 2は左下の呼び出し。
-
ID 3は右下の呼び出し。
注: クアッドID用の組込み値アクセサは存在しません。
通常、フラグメント処理は、RasterizationPointごとにフラグメントシェーダーの呼び出しを1つ生成します(ラスタライズ参照)。 グラフィックスプリミティブの端などでは、クアッドを完全に埋めるだけのRasterizationPointが不足する場合があります。 クアッドに対応するRasterizationPointが1つ、2つ、または3つしかない場合、フラグメント処理はヘルパー呼び出し をそのクアッドの空いている位置ごとに生成します。
ヘルパー呼び出しは、微分の算出を助ける以外に可観測な効果はありません。 そのため、ヘルパー呼び出しには以下の制約が課されます:
-
記述アクセス(§ 14.5.1 メモリ操作も参照)については、 storageやhandleアドレス空間には実行されません。
-
アトミック組込み関数は、不定値を返します。
-
エントリポイントの戻り値は、その後 GPURenderPipelineで 下流処理されません。
クアッド内のすべての呼び出しがヘルパー呼び出しとなった場合(例:discard文の実行による)、 クアッドの実行は終了されることがありますが、そのような終了は非均一な制御フローを生成するものとはみなされません。
15.5. サブグループ
サブグループは、 コンピュートまたはフラグメントシェーダーステージのエントリーポイントを同時に実行し、 データを効率的に共有し集団的に結果を計算できる呼び出しの集合です。 コンピュートまたはフラグメントシェーダーの各呼び出しは正確に1つのサブグループに属します。 コンピュートシェーダーでは、各サブグループは特定のワークグループの部分集合です。 フラグメントシェーダーでは、サブグループは複数の描画コマンドから呼び出しを含む場合があります。 各クアッドは単一のサブグループに含まれます。
サブグループサイズは、 サブグループ内の最大呼び出し数です。 シェーダー内では、subgroup_size組込み値で参照できます。 サブグループサイズは、ディスパッチコマンドおよびワークグループ内で均一な値ですが、描画コマンド内では均一値でない場合があります。 全てのサブグループサイズは[4, 128]の範囲内の2のべき乗であり、特定のデバイス向けにコンパイルされたシェーダーの値はsubgroupMinSizeから subgroupMaxSizeまでの範囲となります(WebGPU § 4.3 GPUAdapter参照)。 実際のサイズはシェーダー、デバイス特性、デバイスコンパイラに依存します。 各デバイスは可能なサブグループサイズ範囲の部分集合(1つだけの場合もある)をサポートします。 デバイスコンパイラは様々なヒューリスティクスで対応サイズから選択します。 サブグループ内の呼び出し数が報告されたサブグループサイズより少ない場合(例:起動数がサブグループサイズ未満)、サブグループはそれより少ない呼び出し数となることがあります。
呼び出しのサブグループ呼び出しIDは、サブグループ内で一意なIDです。
このIDはsubgroup_invocation_id組込み値で参照でき、範囲は[0,
subgroup_size
- 1]です。
subgroup_invocation_id
とlocal_invocation_indexの間に定義された関係はありません。
非移植的なコードを避けるため、シェーダー作者はこれら2つの値の特定のマッピングを仮定すべきではありません。
同じサブグループ内の呼び出しが異なる制御フローパスを実行するとき、サブグループ実行が分岐した(diverge)と言います。 これは非均一な制御フローの特別なケースです。 分岐はサブグループ操作の意味に影響します。 サブグループ操作を同時に実行するサブグループ内の呼び出しは、その操作に対してアクティブです。 サブグループ内のその他の呼び出しは、その操作に対して非アクティブです。 サブグループサイズがサブグループ内の呼び出し数を超える場合、余分な仮想呼び出しは非アクティブとみなされます。 ヘルパー呼び出しは操作に対してアクティブにも非アクティブにもなり得ます。 つまり、デバイスによってはヘルパー呼び出しがサブグループ操作に参加する場合もあれば、しない場合もあります。
注: 非均一な制御フローで動作する場合、基盤デバイス間で非移植性が大きく、デバイスコンパイラはこのようなコードを積極的に最適化することが多いです。 その結果、サブグループにはシェーダー作者が想定したものと異なるアクティブ呼び出し集合が含まれる場合があります。
15.6. 集団演算
15.6.1. バリア
バリアはプログラム内のメモリ操作の順序を制御する同期組み込み関数です。 制御バリアは、同じワークグループ内のすべての呼び出しによって、あたかも同時に実行されるかのように実行されます。 このため、制御バリアはコンピュートシェーダーの一様な制御フローでのみ実行しなければなりません。
15.6.2. 微分
偏微分は、ある軸方向の値の変化率です。 同じクアッド内のフラグメントシェーダー呼び出しが協調して近似的な偏微分を計算します。
微分を計算する組込み関数は以下です:
フラグメント座標の偏微分は、以下の組込み関数の動作の一部として暗黙的に計算されます:
これらの場合、微分結果はサンプリングされるテクセルのミップレベルを決定したり、textureSampleCompare
の場合はサンプリング値を参照値と比較する際に使われます。
呼び出し指定値の偏微分は、§ 17.6 微分組込み関数で説明される組込み関数によって計算されます:
-
dpdx、dpdxCoarse、dpdxFineはx軸方向の偏微分を計算します。
-
dpdy、dpdyCoarse、dpdyFineはy軸方向の偏微分を計算します。
-
fwidth、fwidthCoarse、fwidthFineは、関連するxおよびyの偏微分を使ってマンハッタン距離を計算します。
隣接する呼び出しが協調して微分を計算するため、これらの関数はフラグメントシェーダー内で均一な制御フローでのみ呼び出すべきです。 これらの関数を呼び出すたびに、derivative_uniformityの診断が、均一性解析で呼び出しが均一な制御フローであることが証明できない場合に発生します。
これらの関数が非均一な制御フローで呼び出された場合、結果は不定値となります。
注: 微分は暗黙のクアッド演算の一種です。 利用にはsubgroups拡張は不要です。
15.6.3. サブグループ演算
サブグループ組込み関数により、サブグループ内の呼び出し間で効率的な通信と計算が可能となります。 サブグループ演算はSIMT(single-instruction multiple-thread)演算です。
アクティブな呼び出しがサブグループ内で通信し、結果を決定します。 そのため、これらの関数は全呼び出しがアクティブなとき(すなわちサブグループレベルの均一な制御フローで)呼び出すと可搬性が最大化されます。
15.6.4. クアッド演算
クアッド組込み関数は、クアッド呼び出しに対して作用します。 これらはクアッド間のデータ通信に有用です。
アクティブな呼び出しがクアッド内で通信し、結果を決定します。 そのため、これらの関数は全呼び出しがアクティブなとき(すなわちクアッドレベルの均一な制御フローで)呼び出すと可搬性が最大化されます。
15.7. 浮動小数点数の評価
WGSL の浮動小数点機能は IEEE-754 標準を基にしていますが、GPU における妥協点を反映して機能が削減されており、移植性を高める追加のガードレールも設けられています。
15.7.1. IEEE-754 の概要
WGSL の浮動小数点型は IEEE-754 のバイナリ浮動小数点型に基づいています。
IEEE-754 バイナリ浮動小数点型は、拡張実数の数直線を次のように近似します:
-
この型は有限個の値の集合を持ち、次の異なるカテゴリが含まれます:
-
この型は次のような演算をサポートします:
-
この型のビット表現は次の特徴を持ちます:
-
固定ビット幅を持ち、各値のビット表現は、最上位ビットから最下位ビットに向かって連続する3つのビットフィールドで構成されます:
-
1ビットの 符号フィールド。
-
固定幅の 指数フィールド。
-
固定幅の 仮数部フィールド。
-
-
指数フィールド の解釈に関係する整数値の 指数バイアス。
-
有限範囲 とは、浮動小数点型の 区間 [low, high] であり、low がその型で最も小さい有限値、high が最も大きい有限値です。
注目すべき IEEE-754 の浮動小数点型は次の通りです:
-
binary16:
-
binary32:
-
binary64:
次のアルゴリズムは、浮動小数点値のビット表現を対応する 拡張実数値または NaN に対応付けます:
アルゴリズム: ビットの浮動小数点解釈入力: Bits は、バイナリ浮動小数点型の値のビット表現。
出力: F は Bits で表される浮動小数点値。
手順:
bias をその型の 指数バイアス とする。
tsw をその型の 仮数部フィールド のビット幅とする。
それぞれのフィールドの解釈値を Sign、E、T とする(いずれも符号なし整数)。
指数フィールド が全て1の場合:
Sign = 0 かつ T = 0 のとき、結果 F = +∞。
Sign = 1 かつ T = 0 のとき、結果 F = −∞。
T ≠ 0 のとき、結果 F は NaN。
それ以外で 指数フィールド が全て0の場合:
結果 F = (−1)Sign × 2−bias × T × 2−tsw+1。
T = 0 の場合、その値はゼロ。
各浮動小数点型には正のゼロと負のゼロが存在します。 負のゼロは、符号ビットが
1
であるゼロ値です。 負のゼロと正のゼロは等値比較で等しいとみなされます。 IEEE-754 では負のゼロは WGSL では重要でない境界ケースを示すために使用されます。T ≠ 0 の場合、値 F は 非正規化 となります。 (デノーマライズ は非正規化の同義語です。)
それ以外の場合、指数フィールド が全て1でも全て0でもない場合:
結果 F = (−1)Sign × 2(E−bias) × ( 1 + T × 2−tsw )。
値 F は 正規化 です。
浮動小数点演算の 定義域 とは、演算がよく定義される 拡張実数 の入力集合です。
-
例えば、数学関数 √ の定義域は区間 [0, +∞] です:√は 0 未満の入力には定義されません。
-
定義域内で評価される場合、演算は無限精度の 拡張実数 中間結果に基づいて定義され、それが 丸めによって浮動小数点値に変換されます。
-
定義域外で評価される場合、 IEEE-754 の既定の例外処理規則により、実装は 例外 を発生させ、NaN を返すことが要求されます。 対照的に、WGSL では浮動小数点例外を義務付けておらず、代わりに 不定値 を返してもよいです。詳しくは § 15.7.2 IEEE-754 との相違点 を参照してください。
丸めは、拡張実数値 x を浮動小数点型の値 x' に対応付けます。 x が浮動小数点型の値である場合、丸めは x をそのまま x' に写します:x = x'。 x が型の 有限範囲 の外側にある場合、丸めはオーバーフローを引き起こします。 それ以外の場合、x' は x より大きい最も小さい浮動小数点値、あるいは x より小さい最大の浮動小数点値となります; どちらが選ばれるかは 丸めモード によって決まります。
一般に、NaN の入力を持つ演算は NaN を返します。 例外として:
-
NaN は他の浮動小数点値と等しい、未満、大きいのいずれにもなりません。これらの比較は偽となります。
IEEE-754 では5種類の 例外が定義されています:
-
不正な演算。 これは、拡張実数の定義域外の入力に対して演算が評価された場合に発生します。 このような演算は NaN を返します。 例:0 × +∞、
sqrt
(−1)。 -
ゼロ除算。 これは、有限なオペランドに対して演算が厳密に無限大の結果となる場合に発生します。 例:1 ÷ 0、log(0)。
-
オーバーフロー。これは、中間結果が型の有限範囲を超えた場合に発生します。詳細は § 15.7.3 浮動小数点の丸めとオーバーフロー を参照してください。
-
不正確。これは、丸め後の結果が 中間結果 と異なる場合、 またはオーバーフローが発生した場合に発生します。
15.7.2. IEEE-754 との相違点
WGSL は IEEE-754 標準に準拠していますが、以下の点で異なります:
-
浮動小数点値 x を整数型に変換する場合、 x はまず変換先型の値の範囲にクランプされます。詳しくは § 15.7.6 浮動小数点変換 を参照してください。
-
浮動小数点例外は生成されません。
-
シグナリング NaN は生成されない場合があります。 中間計算では、シグナリング NaN がクワイエット NaN に変換されることがあります。
-
有限数学仮定:
-
オーバーフロー、無限大、およびNaNがシェーダー実行前に生成された場合、エラーが発生します。
-
const式やoverride式で有限値に対して演算を行うと、IEEE-754のルールに従い、オーバーフロー、無限大、NaNが中間結果として生成されます。
-
注: このルールは、これらの種類の式に対し、オーバーフロー、無限大、NaNを精度制限内で確実に検出し、エラーを一貫して生成できるように実装することを要求します。
-
-
浮動小数点型のconst式がオーバーフローしたり、NaNまたは無限大になる場合、シェーダー作成エラーとなります。
-
浮動小数点型のoverride式がオーバーフローしたり、NaNまたは無限大になると、パイプライン作成エラーとなります。
-
-
実装によっては、シェーダー実行中にオーバーフロー、無限大、NaNが存在しないと仮定する場合があります。
-
-
実装は浮動小数点ゼロ値の符号フィールドを無視しても構いません。 つまり、正符号付きゼロが負符号付きゼロと同様に振る舞うことがあります。
-
ゼロへのフラッシュとは、浮動小数点型の非正規化値をその型のゼロ値に置き換えることです。
-
§ 15.7.4 浮動小数点精度に記載された演算の入力や出力はゼロへフラッシュされることがあります。
-
さらに、中間結果値は § 17.2 ビット再解釈組み込み関数、§ 17.9 データパック組み込み関数、§ 17.10 データアンパック組み込み関数の演算でゼロへフラッシュされることがあります。
-
他の演算は非正規化数値を保持する必要があります。
-
-
演算の精度は§ 15.7.4 浮動小数点精度で規定されています。
-
WGSL の一部の組み込み関数は、対応する IEEE-754 演算と異なる意味を持ちます。 そのような場合は、WGSL の組み込み関数定義で必要に応じて記載されています。
例えば WGSL の § 17.5.32 fma 関数は、通常の乗算(丸めステップを含む)と加算(さらに丸めステップ)として展開されることがありますが、IEEE-754 の
fusedMultiplyAdd
演算は最終の丸めステップのみを要求します。
15.7.3. 浮動小数点の丸めとオーバーフロー
オーバーフローする計算は、無限大または最も近い有限値へ丸められることがあります。 結果は、オーバーフローした中間結果値の大きさと、評価がシェーダーモジュール生成、パイプライン生成、またはシェーダー実行中かによって異なります。
浮動小数点型 T に対し、MAX(T) を T の最大正有限値、 2EMAX(T) を T で表現可能な最大の 2 のべき乗値と定義します。 特に EMAX(f32) = 127, EMAX(f16) = 15 です。
X を浮動小数点計算から得られる無限精度の中間結果とします。 式の最終値は、中間結果値 X' および X'' を介して、次のように2段階で決定されます:
X から T で丸めて X' を計算します:
-
X が 有限範囲内なら、X' は X を切り上げまたは切り下げした結果です。
-
X が NaN の場合、X' も NaN です。
-
MAX(T) < X < 2EMAX(T)+1 の場合、どちらの丸め方向も可能です:X' は MAX(T) または +∞ です。
-
2EMAX(T)+1 ≤ X の場合、X' = +∞ です。
-
注: この条件は IEEE-754 の規則に一致します。
-
-
−MAX(T) > X > −2EMAX(T)+1 の場合、どちらの丸め方向も可能です:X' は −MAX(T) または −∞ です。
-
−2EMAX(T)+1 ≥ X の場合、X' = −∞ です。
-
注: この条件は IEEE-754 の規則に一致します。
-
X' から式の最終値 X'' を計算するか、プログラムエラーを検出します:
-
X' が無限大または NaN の場合は、有限数学仮定に従います:
-
式が定数式なら、シェーダー生成エラーを生成します。
-
式がオーバーライド式なら、パイプライン生成エラーを生成します。
-
-
それ以外の場合、X'' = X' です。
15.7.4. 浮動小数点精度
-
x が T に含まれる場合は x、
-
それ以外の場合:
-
x より大きい T の最小値、または
-
x より小さい T の最大値。
-
つまり、結果は丸めによって切り上げまたは切り下げされます: WGSL では丸めモードは指定されません。
注: 浮動小数点型には正および負の無限大が含まれるため、 正しく丸められた結果は有限値または無限大になる場合があります。
注: 無限精度で計算された演算結果は、ダブルの精度を超える精度が必要となる場合があります。
その一例として x - y
で x=1.0
、y=1.17e-38
(最小の正の正規化単精度浮動小数点数)の場合です。
これらの数値は指数が 126
離れています。IEEE-754 の binary64(倍精度)形式は仮数部が52ビットしかないため、
この減算では y
の有効桁がすべて失われます。
丸めモードによっては、y
が小さいが 0 でない場合、WGSL の式 x - y
は x
と同じ値になることがあります。
[ECMASCRIPT] では IEEE-754 の
roundTiesToEven 丸めモードに相当するものが使われています。
浮動小数点数 x
の最小単位 ULP
は以下のように定義されます [Muller2005]:
-
x
が浮動小数点型の有限範囲内にある場合、ULP(x) はa
≤x
≤b
となる二つの異なる有限浮動小数点数a
,b
の最小距離ulp(x) = min
a,b
|b - a|
です。 -
それ以外の場合、ULP(x) は
b
およびa
(最大および二番目に大きい表現可能な有限浮動小数点値)の差|b - a|
です。
演算の精度は次の5通りのいずれかで示されます:
-
(非浮動小数点結果値に対して)正確な結果。
-
正しく丸められる。
-
絶対誤差の上限。
-
ULP で表現される相対誤差の上限。
-
精度が 継承されることを示す式。 すなわち、演算の精度は与えられた WGSL 式の評価精度として定義されます。 与えられた式は、その関数の正しい実装のひとつです。
継承元の式を評価する際、部分式の評価には浮動小数点評価の他の規則( 丸め、 オーバーフロー、 再結合、 融合、 ゼロへのフラッシュなど)も適用されます。
WebGPU の実装は演算を異なる方法で実装し、より高い精度や極端な入力への許容度を持つことがあります。
演算の精度が入力範囲について指定されている場合、 その範囲外の入力値に対する精度は未定義です。
許容される結果が結果型の有限範囲外の場合、§ 15.7.3 浮動小数点の丸めとオーバーフローの規則が適用されます。
15.7.4.1. 具体的な浮動小数点式の精度
式 | f32 の精度 | f16 の精度 |
---|---|---|
x + y
| 正しく丸め | |
x - y
| 正しく丸め | |
x * y
| 正しく丸め | |
x / y
| 2.5 ULP(|y| が [2-126, 2126] の範囲)
| 2.5 ULP(|y| が [2-14, 214] の範囲)
|
x % y
| x - y * trunc(x/y) から継承
| |
-x
| 正しく丸め | |
x == y
| 正確な結果 | |
x != y
| 正確な結果 | |
x < y
| 正確な結果 | |
x <= y
| 正確な結果 | |
x > y
| 正確な結果 | |
x >= y
| 正確な結果 |
組み込み関数 | f32 の精度 | f16 の精度 |
---|---|---|
abs(x)
| 正しく丸め | |
acos(x)
|
より悪い方:
|
より悪い方:
|
acosh(x)
| log(x + sqrt(x * x - 1.0)) から継承
| |
asin(x)
|
より悪い方:
|
より悪い方:
|
asinh(x)
| log(x + sqrt(x * x + 1.0)) から継承
| |
atan(x)
| 4096 ULP | 5 ULP |
atan2(y, x)
| |x| が [2-126, 2126] の範囲で、y が有限で正規化の場合 4096 ULP
| |x| が [2-14, 214] の範囲で、y が有限で正規化の場合 5 ULP
|
atanh(x)
|
log( (1.0 + x) / (1.0 - x) ) * 0.5 から継承
| |
ceil(x)
| 正しく丸め | |
clamp(x,low,high)
|
正しく丸め。
無限精度の結果は
| |
cos(x)
| x が区間 [-π, π] の場合、絶対誤差最大 2-11
| x が区間 [-π, π] の場合、絶対誤差最大 2-7
|
cosh(x)
| (exp(x) + exp(-x)) * 0.5 から継承
| |
cross(x, x)
| (x[i] * y[j] - x[j] * y[i]) (i ≠
j )から継承
| |
degrees(x)
| x * 57.295779513082322865 から継承
| |
determinant(m:mat2x2<T>) determinant(m:mat3x3<T>) determinant(m:mat4x4<T>)
|
無限 ULP。
注:WebGPU の実装は実用的な行列式関数を提供すべきです。
理想的な数学では、行列式は加算、減算、乗算で計算されます。 しかし、GPU は浮動小数点演算を用い、GPU 実装の行列式はオーバーフローや誤差への堅牢性よりも速度と単純さを重視します。 例えば、2x2 行列式の素朴な計算
( WGSL の行列式に有限な誤差境界がないのは、基盤となる実装にも同じ制約があるためです。 | |
distance(x, y)
| length(x - y) から継承
| |
dot(x, y)
| x[i] * y[i] の合計から継承
| |
dpdx(x) dpdxCoarse(x) dpdxFine(x) dpdy(x) dpdyCoarse(x) dpdyFine(x) fwidth(x) fwidthCoarse(x) fwidthFine(x) |
無限 ULP。
注:WebGPU の実装は実用的な微分関数を提供すべきです。
微分は、GPU 上の異なる呼び出し間の値の差( WGSL の微分に有限な誤差境界がないのは、基盤となる実装にも同じ制約があるためです。 | |
exp(x)
| 3 + 2 * |x| ULP
| 1 + 2 * |x| ULP
|
exp2(x)
| 3 + 2 * |x| ULP
| 1 + 2 * |x| ULP
|
faceForward(x, y, z)
| select(-x, x, dot(z, y) < 0.0) から継承
| |
floor(x)
| 正しく丸め | |
fma(x, y, z)
| x * y + z から継承
| |
fract(x)
| x - floor(x) から継承
| |
frexp(x)
| x がゼロまたは正規化時、正しく丸め
| |
inverseSqrt(x)
| 2 ULP | |
ldexp(x, y)
| 正しく丸め | |
length(x)
| ベクトルの場合 sqrt(dot(x, x)) 、スカラーの場合
sqrt(x*x) から継承
| |
log(x)
| x が区間 [0.5, 2.0] の場合、絶対誤差最大 2-21x が区間 [0.5, 2.0] の外の場合 3 ULP | x が区間 [0.5, 2.0] の場合、絶対誤差最大 2-7x が区間 [0.5, 2.0] の外の場合 3 ULP |
log2(x)
| x が区間 [0.5, 2.0] の場合、絶対誤差最大 2-21x が区間 [0.5, 2.0] の外の場合 3 ULP | x が区間 [0.5, 2.0] の場合、絶対誤差最大 2-7x が区間 [0.5, 2.0] の外の場合 3 ULP |
max(x, y)
|
正しく丸め
| |
min(x, y)
|
正しく丸め
| |
mix(x, y, z)
| x * (1.0 - z) + y * z から継承
| |
modf(x)
| 正しく丸め | |
normalize(x)
| x / length(x) から継承
| |
pack4x8snorm(x)
| 正しく丸められた中間結果値。正確な結果。 | |
pack4x8unorm(x)
| 正しく丸められた中間結果値。正確な結果。 | |
pack2x16snorm(x)
| 正しく丸められた中間結果値。正確な結果。 | |
pack2x16unorm(x)
| 正しく丸められた中間結果値。正確な結果。 | |
pack2x16float(x)
| 正しく丸められた中間結果値。正確な結果。 | |
pow(x, y)
| exp2(y * log2(x)) から継承
| |
quantizeToF16(x)
| 正しく丸め | |
radians(x)
| x * 0.017453292519943295474 から継承
| |
reflect(x, y)
| x - 2.0 * dot(x, y) * y から継承
| |
refract(x, y, z)
| z * x - (z * dot(y, x) + sqrt(k)) * y
から継承、ここで k = 1.0 - z * z * (1.0 - dot(y, x) * dot(y, x)) k < 0.0
の場合、結果は厳密に 0.0
| |
round(x)
| 正しく丸め | |
sign(x)
| 正しく丸め | |
sin(x)
| x が区間 [-π, π] の場合、絶対誤差最大 2-11
| x が区間 [-π, π] の場合、絶対誤差最大 2-7
|
sinh(x)
| (exp(x) - exp(-x)) * 0.5 から継承
| |
saturate(x)
| 正しく丸め | |
smoothstep(edge0, edge1, x)
| t * t * (3.0 - 2.0 * t) から継承、ここで t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0)
| |
sqrt(x)
| 1.0 / inverseSqrt(x) から継承
| |
step(edge, x)
| 正しく丸め | |
tan(x)
| sin(x) / cos(x) から継承
| |
tanh(x)
|
より悪い方:
| |
transpose(x)
| 正しく丸め | |
trunc(x)
| 正しく丸め | |
unpack4x8snorm(x)
| 3 ULP | N/A |
unpack4x8unorm(x)
| 3 ULP | N/A |
unpack2x16snorm(x)
| 3 ULP | N/A |
unpack2x16unorm(x)
| 3 ULP | N/A |
unpack2x16float(x)
| 正しく丸め | N/A |
subgroupBroadcast(x, i)
| 正しく丸め | |
subgroupBroadcastFirst(x)
| 正しく丸め | |
subgroupAdd(x)
| アクティブなサブグループ呼び出し全ての x の合計から継承 | |
subgroupExclusiveAdd(x)
| アクティブなサブグループ呼び出しのうち、サブグループ呼び出し ID が現在の ID 未満のものの x の合計から継承 | |
subgroupInclusiveAdd(x)
| アクティブなサブグループ呼び出しのうち、サブグループ呼び出し ID が現在の ID 以下のものの x の合計から継承 | |
subgroupMul(x)
| アクティブなサブグループ呼び出し全ての x の積から継承 | |
subgroupExclusiveMul(x)
| アクティブなサブグループ呼び出しのうち、サブグループ呼び出し ID が i 番目呼び出しの ID 未満の xi の積から継承 | |
subgroupInclusiveMul(x)
| アクティブなサブグループ呼び出しのうち、サブグループ呼び出し ID が i 番目呼び出しの ID 以下の xi の積から継承 | |
subgroupMax(x)
| アクティブなサブグループ呼び出し全ての max(x) から継承 | |
subgroupMin(x)
| アクティブなサブグループ呼び出し全ての min(x) から継承 | |
subgroupShuffle(x, id)
| 正しく丸め | |
subgroupShuffleDown(x, delta)
| 正しく丸め | |
subgroupShuffleUp(x, delta)
| 正しく丸め | |
subgroupShuffleXor(x, mask)
| 正しく丸め | |
quadBroadcast(x, id)
| 正しく丸め | |
quadSwapDiagonal(x)
| 正しく丸め | |
quadSwapX(x)
| 正しく丸め | |
quadSwapY(x)
| 正しく丸め |
15.7.4.2. AbstractFloat 式の精度
AbstractFloat演算の精度は以下の通りです:
-
対応するf32演算で正確な結果が要求される場合、正確な結果が必要です。
-
fract(x)
の誤差はx - floor(x)
から継承され、中間計算は AbstractFloat 演算として行われます。 -
それ以外の場合、対応するf32演算の誤差は絶対誤差、相対誤差、実装由来の誤差、またはそれらの組み合わせです。 この場合、AbstractFloatの誤差は無制限です。
-
ただし、AbstractFloat演算の誤差は、絶対値で対応するf32演算の誤差を超えないべきです。
-
この推奨は、型を f32 から AbstractFloat に変更したときに精度が低下しないようにするためのものです。
-
演算は WebAssembly [WASM-CORE-2] や ECMAScript [ECMASCRIPT] 環境で評価されることがあり、それらの仕様では多くの数値計算に誤差境界が定められていません。 例えば ECMAScript では多くの浮動小数点演算が 実装依存近似とされています。 実装は理想に近づくよう努力が推奨されていますが、厳密な要件はありません。
-
ULP は AbstractFloat値の場合、 AbstractFloat が IEEE-754 の binary64型と同一であると仮定します。
f32値における1ULPは、IEEE-754 binary64値の1ULPの229倍の大きさです。 これは binary64 の仮数部が f32 より 29ビット長いからです。
例えば、演算の真の結果値が x だが、計算結果が x' となった場合、 その誤差 x-x' が f32 で 3ULP なら、同じ絶対誤差 x-x' は AbstractFloat では 3·229 ULP となります。
15.7.5. 再結合と融合
再結合とは、式中の演算の順序を変更し、厳密に計算した場合に同じ答えが得られるようにすることです。例:
-
(a + b) + c
はa + (b + c)
に再結合されます -
(a - b) + c
は(a + c) - b
に再結合されます -
(a * b) / c
は(a / c) * b
に再結合されます
しかし、浮動小数点で計算すると結果が異なる場合があります。 再結合された結果は近似による不正確さや、中間結果の計算でオーバーフローや NaN を引き起こす可能性があります。
実装は演算を再結合しても構いません。
実装は、変換後の式が元の形式と同等以上に精度が高ければ、演算を融合しても構いません。 例えば、融合乗算加算(fused multiply-add)実装は、乗算と加算を個別に行うより高精度な場合があります。
15.7.6. 浮動小数点変換
この節では、浮動小数点型がソースまたは変換先となるスカラー変換の詳細を説明します。
この節でいう浮動小数点型は、次のいずれかです:
-
WGSL の f32、f16、AbstractFloat 型。
-
IEEE-754 浮動小数点標準で定義されたバイナリ形式に対応する仮想型。
注: f32 WGSL 型は IEEE-754 の binary32 形式、f16 WGSL 型は IEEE-754 の binary16 形式に対応します。
スカラー浮動小数点から整数型への変換アルゴリズム:
浮動小数点スカラー値 X を整数スカラー型 T に変換するには:
注: NaN 以外の場合、浮動小数点から整数への変換は 変換先型の範囲内に値をクランプし、ゼロ方向に丸めます。 このクランプ要件は WGSL が意味のある結果を保証する箇所であり、C や C++ では未定義動作となり、 IEEE-754 では不正な演算例外と NaN が要求される場面です。
数値スカラーから浮動小数点への変換アルゴリズム:
アルゴリズム: 数値スカラー変換から浮動小数点へ入力:
X:型 S の数値スカラー値
T:変換先の浮動小数点型
出力: XOut:X を型 T に変換した結果、またはエラー
手順:
X が元の型 S の NaN なら、XOut は型 T の NaN。
X が変換先型 T に正確に表現できる場合、XOut は X と等しい T の値。
それ以外の場合、X は T で正確に表現できない:
X が T の隣接する有限値の間にある場合、XOut はそのいずれか。 WGSL は高い方か低い方か指定せず、変換ごとに異なる場合もある。
それ以外で、X が変換先型 T の有限範囲外の場合:
Xの式が定数式ならシェーダー生成エラーとなる。
Xの式がオーバーライド式ならパイプライン生成エラーとなる。
それ以外の場合、変換は以下の通り:
X' を元の値 X に設定する。
ソース型 S が変換先型 T より多くの仮数部ビットを持つ浮動小数点型の場合、 ソース値 X の余分な仮数部ビットは破棄してもよい(0扱い)。 X' を更新。
X' が変換先型 T の最大または最小有限値なら XOut = X'。
それ以外の場合、XOut は変換先型 T の無限大値(符号は X' に合わせる)。
注: 整数値が2つの隣接する浮動小数点値の間に位置する場合もあります。 特に f32 型は 23ビットの仮数部を持ちます。 さらに、浮動小数点値が正規化範囲内(指数が極端でない)なら、仮数部は 小数部ビットに加え最上位の23ビット目に1ビットが追加されます。 例えば整数 228 と 1+228 は同じ浮動小数点値にマッピングされます。 最下位1ビットの差が浮動小数点型で表現できません。 このような衝突は絶対値が 225 以上の隣接整数のペアで発生します。
注: 元の型が i32 または u32 で、変換先型が f32 の場合、元の値は必ず変換先型の範囲内です。
注: ソース型がターゲット浮動小数点型より指数部・仮数部ビットが少ない場合、元の値は必ず変換先型の範囲内です。
15.7.7. 浮動小数点式および組み込み関数の定義域
前節では、浮動小数点式が定義域外で評価された場合の期待される挙動を説明しています。
§ 8.7 算術式と§ 17.5 数値組み込み関数では、浮動小数点式と組み込み関数の定義域を定義しています。 特に制限がない場合、その演算の定義域は全ての有限・無限入力を含みます。 それ以外の場合は明示的な定義域が記載されています。
多くの場合、WGSL の演算が IEEE-754 で定義されている演算に対応している場合、定義域も一致します。
例えば WGSL および IEEE-754 の acos
演算は定義域が [−1,1] です。
要素単位演算で明示的な定義域がある場合は、スカラーケースのみ記載されます。ベクトルの場合は要素単位の意味から推論されます。
WGSL の一部の演算は他の WGSL 式で実装される場合があります。 § 15.7.4 浮動小数点精度では、そのような演算の精度を継承としています。 これらの演算の定義域を列挙する際は、以下のいずれかとなります:
-
定義域が明示的に記載されている場合
-
定義域が 線形項から推論とされている場合、定義域は次のように導かれます:
-
元の演算を「継承」式で置き換え、加算・減算・乗算の組み合わせとして扱う。
-
残った演算の定義域制約をパラメータに適用・合成する。
-
例:2要素ベクトル a、b に対する dot(a,b)
関数の精度は、式
a[0] * b[0] + a[1] * b[1] から継承されます。
これは2回の浮動小数点乗算と1回の浮動小数点加算を利用します。
したがって、定義域は以下を除く拡
-
浮動小数点の乗算は、片方のオペランドがゼロでもう片方が無限大の場合を除き、拡張実数上でよく定義されています。
-
浮動小数点の加算は、二つのオペランドが符号が逆の無限大の場合を除き、よく定義されています。
-
したがって、定義域は拡張実数の2要素ベクトル a と b の全ての組み合わせですが、以下を除きます:
-
乗算から導かれるもの:
-
a[i] がゼロで b[i] が無限大の場合。
-
a[i] が無限大で b[i] がゼロの場合。
-
-
加算から導かれるもの:
-
a[0] × b[0] が +∞ かつ a[1] × b[1] が +∞ の場合。
-
a[0] × b[0] が −∞ かつ a[1] × b[1] が −∞ の場合。
-
-
16. キーワードとトークンの概要
16.1. キーワードの概要
-
alias
-
break
-
case
-
const
-
const_assert
-
continue
-
continuing
-
default
-
diagnostic
-
discard
-
else
-
enable
-
false
-
fn
-
for
-
if
-
let
-
loop
-
override
-
requires
-
return
-
struct
-
switch
-
true
-
var
-
while
16.2. 予約語
予約語は、将来使用のために予約されているトークンです。 WGSLモジュールは予約語を含めてはなりません。
以下は予約語です:
| 'NULL'
| 'Self'
| 'abstract'
| 'active'
| 'alignas'
| 'alignof'
| 'as'
| 'asm'
| 'asm_fragment'
| 'async'
| 'attribute'
| 'auto'
| 'await'
| 'become'
| 'cast'
| 'catch'
| 'class'
| 'co_await'
| 'co_return'
| 'co_yield'
| 'coherent'
| 'column_major'
| 'common'
| 'compile'
| 'compile_fragment'
| 'concept'
| 'const_cast'
| 'consteval'
| 'constexpr'
| 'constinit'
| 'crate'
| 'debugger'
| 'decltype'
| 'delete'
| 'demote'
| 'demote_to_helper'
| 'do'
| 'dynamic_cast'
| 'enum'
| 'explicit'
| 'export'
| 'extends'
| 'extern'
| 'external'
| 'fallthrough'
| 'filter'
| 'final'
| 'finally'
| 'friend'
| 'from'
| 'fxgroup'
| 'get'
| 'goto'
| 'groupshared'
| 'highp'
| 'impl'
| 'implements'
| 'import'
| 'inline'
| 'instanceof'
| 'interface'
| 'layout'
| 'lowp'
| 'macro'
| 'macro_rules'
| 'match'
| 'mediump'
| 'meta'
| 'mod'
| 'module'
| 'move'
| 'mut'
| 'mutable'
| 'namespace'
| 'new'
| 'nil'
| 'noexcept'
| 'noinline'
| 'nointerpolation'
| 'non_coherent'
| 'noncoherent'
| 'noperspective'
| 'null'
| 'nullptr'
| 'of'
| 'operator'
| 'package'
| 'packoffset'
| 'partition'
| 'pass'
| 'patch'
| 'pixelfragment'
| 'precise'
| 'precision'
| 'premerge'
| 'priv'
| 'protected'
| 'pub'
| 'public'
| 'readonly'
| 'ref'
| 'regardless'
| 'register'
| 'reinterpret_cast'
| 'require'
| 'resource'
| 'restrict'
| 'self'
| 'set'
| 'shared'
| 'sizeof'
| 'smooth'
| 'snorm'
| 'static'
| 'static_assert'
| 'static_cast'
| 'std'
| 'subroutine'
| 'super'
| 'target'
| 'template'
| 'this'
| 'thread_local'
| 'throw'
| 'trait'
| 'try'
| 'type'
| 'typedef'
| 'typeid'
| 'typename'
| 'typeof'
| 'union'
| 'unless'
| 'unorm'
| 'unsafe'
| 'unsized'
| 'use'
| 'using'
| 'varying'
| 'virtual'
| 'volatile'
| 'wgsl'
| 'where'
| 'with'
| 'writeonly'
| 'yield'
16.3. 構文トークン
構文トークンは、 特殊なコードポイントの並びであり、以下の目的で使用されます:
-
式演算子を表記するため、または
-
句読点として:他の文法要素をグループ化、並べる、または区切るため。
構文トークンは次の通りです:
-
'&'
(コードポイント:U+0026
) -
'&&'
(コードポイント:U+0026
U+0026
) -
'->'
(コードポイント:U+002D
U+003E
) -
'@'
(コードポイント:U+0040
) -
'/'
(コードポイント:U+002F
) -
'!'
(コードポイント:U+0021
) -
'['
(コードポイント:U+005B
) -
']'
(コードポイント:U+005D
) -
'{'
(コードポイント:U+007B
) -
'}'
(コードポイント:U+007D
) -
':'
(コードポイント:U+003A
) -
','
(コードポイント:U+002C
) -
'='
(コードポイント:U+003D
) -
'=='
(コードポイント:U+003D
U+003D
) -
'!='
(コードポイント:U+0021
U+003D
) -
'>'
(コードポイント:U+003E
)(また、テンプレート曖昧性解消用_greater_than
) -
'>='
(コードポイント:U+003E
U+003D
)(また、テンプレート曖昧性解消用_greater_than_equal
) -
'>>'
(コードポイント:U+003E
U+003E
)(また、テンプレート曖昧性解消用_shift_right
) -
'<'
(コードポイント:U+003C
)(また、テンプレート曖昧性解消用_less_than
) -
'<='
(コードポイント:U+003C
U+003D
)(また、テンプレート曖昧性解消用_less_than_equal
) -
'<<'
(コードポイント:U+003C
U+003C
)(また、テンプレート曖昧性解消用_shift_left
) -
'%'
(コードポイント:U+0025
) -
'-'
(コードポイント:U+002D
) -
'--'
(コードポイント:U+002D
U+002D
) -
'.'
(コードポイント:U+002E
) -
'+'
(コードポイント:U+002B
) -
'++'
(コードポイント:U+002B
U+002B
) -
'|'
(コードポイント:U+007C
) -
'||'
(コードポイント:U+007C
U+007C
) -
'('
(コードポイント:U+0028
) -
')'
(コードポイント:U+0029
) -
';'
(コードポイント:U+003B
) -
'*'
(コードポイント:U+002A
) -
'~'
(コードポイント:U+007E
) -
'_'
(コードポイント:U+005F
) -
'^'
(コードポイント:U+005E
) -
'+='
(コードポイント:U+002B
U+003D
) -
'-='
(コードポイント:U+002D
U+003D
) -
'*='
(コードポイント:U+002A
U+003D
) -
'/='
(コードポイント:U+002F
U+003D
) -
'%='
(コードポイント:U+0025
U+003D
) -
'&='
(コードポイント:U+0026
U+003D
) -
'|='
(コードポイント:U+007C
U+003D
) -
'^='
(コードポイント:U+005E
U+003D
) -
'>>='
(コードポイント:U+003E
U+003E
U+003D
)(また、テンプレート曖昧性解消用_shift_right_assign
) -
'<<='
(コードポイント:U+003C
U+003C
U+003D
)(また、テンプレート曖昧性解消用_shift_left_assign
) -
_template_args_end
-
テキスト:
'>'
(コードポイント:U+003E
) -
このトークンはgreater_than 構文トークンと同じテキストです。
-
テンプレートリスト曖昧性解消によって生成され、テンプレートリストの最後のトークンとして使用されます。
-
-
_template_args_start
-
テキスト:
'<'
(コードポイント:U+003C
) -
このトークンはless_than構文トークンと同じテキストです。
-
テンプレートリスト曖昧性解消によって生成され、テンプレートリストの最初のトークンとして使用されます。
-
-
_disambiguate_template
-
テキスト: なし
-
このトークンはパーサにテンプレートリストの走査を通知します。
-
テンプレートリスト曖昧性解消をトリガーします。
-
17. 組み込み関数
いくつかの関数は事前宣言されており、 実装によって提供されるため、WGSLモジュールで常に利用可能です。 これらは組み込み関数と呼ばれます。
組み込み関数は、同じ名前を持つ関数群であり、 それぞれは仮引数の数、順序、型によって区別されます。 それぞれの異なる関数バリエーションはオーバーロードです。
注: 各ユーザー定義関数は 1つのオーバーロードしか持ちません。
各オーバーロードは以下の内容で記述されます:
組み込み関数を呼び出す際、すべての引数は関数の評価が始まる前に評価されます。 § 11.2 関数呼び出しを参照してください。
17.1. コンストラクタ組み込み関数
値コンストラクタ組み込み関数は、 指定した型の値を明示的に生成します。
WGSL は、すべての 事前宣言済み 型およびすべての 構築可能な 構造体型に対して値コンストラクタを提供します。 このようなコンストラクタの組み込み関数は、型名またはその型に対する 型エイリアスと同じスペルになります。 そのような組み込み関数が使用される場合、識別子は、 型または型エイリアスの スコープ内で なければならず、 また 識別子は 他の宣言に 解決されてはならない 必要があります。
注: frexp、 modf、およびatomicCompareExchangeWeakが返す構造体型は WGSLモジュール内で記述できません。
注: その型の値宣言は、そのWGSLテキストのその文において有効でなければなりません。
WGSLは2種類の値コンストラクタを提供します:
-
値コンストラクタ(変換も提供)
17.1.1. ゼロ値組み込み関数
各具体的、構築可能なTは
一意のゼロ値を持ち、
WGSLでは型名の後に空の括弧を付けて記述します:
T ()
。
抽象数値型もゼロ値を持ちますが、
それらにアクセスする組み込み関数はありません。
ゼロ値は以下の通りです:
-
bool()
はfalse
-
i32()
は0i -
u32()
は0u -
f32()
は0.0f -
f16()
は0.0h -
型TのN成分ベクトルのゼロ値は、型Tのゼロ値のN成分ベクトルです。
-
型TのC列R行の行列のゼロ値は、型Tのゼロ値で埋められたその次元の行列です。
-
型Eの要素型を持つ構築可能なN要素配列のゼロ値は、型Eのゼロ値のN要素の配列です。
-
構築可能な構造体型Sのゼロ値は、ゼロ値メンバーを持つ構造体値Sです。
-
AbstractIntのゼロ値は0です。
-
AbstractFloatのゼロ値は0.0です。
注: WGSLにはatomic型、 実行時サイズ配列、またはその他 構築可能ではない型のゼロ値組み込み関数はありません。
オーバーロード |
|
パラメータ化 | T は具体的な構築可能型です。 |
説明 | 型T のゼロ値を構築します。
|
注: AbstractIntのゼロ値ベクトルは
vec2()
、vec3()
、およびvec4()
として記述できます。
vec2< f32> () // 2つのf32成分のゼロ値ベクトル。 vec2< f32> ( 0.0 , 0.0 ) // 同じ値を明示的に記述。 vec3< i32> () // 3つのi32成分のゼロ値ベクトル。 vec3< i32> ( 0 , 0 , 0 ) // 同じ値を明示的に記述。
struct Student { grade : i32, GPA : f32, attendance : array< bool, 4 > } fn func () { var s : Student ; // Student型のゼロ値 s = Student (); // 同じ値を明示的に記述。 s = Student ( 0 , 0.0 , array< bool, 4 > ( false , false , false , false )); // ゼロ値メンバーで記述した同じ値。 s = Student ( i32(), f32(), array< bool, 4 > ()); }
17.1.2. 値コンストラクタ組み込み関数
以下の小節で定義される組み込み関数は、構築可能な値を以下の方法で生成します:
-
同じ型の既存の値をコピー(つまり恒等関数)、または
-
明示的な成分リストから複合値を生成。
-
他の値型から変換。
ベクトルおよび行列型のコンストラクタは、成分やサブベクトルの組み合わせから、成分型が一致するベクトル・行列値を構築します。 ベクトルや行列の構築では、対象型の次元のみを指定し、成分型はコンストラクタ引数から推論されるオーバーロードもあります。
17.1.2.1. array
オーバーロード |
|
パラメータ化 | T は具体的かつ構築可能です
|
説明 |
配列を要素から構築します。
注: array<T,N>は構築可能です。なぜなら、 要素数は コンストラクタ引数の数と等しく、よって シェーダ作成時に完全に決定されるからです。 |
オーバーロード |
|
パラメータ化 | T は構築可能です
|
説明 |
配列を要素から構築します。
成分型は要素の型から推論されます。 配列サイズは要素数によって決まります。 |
17.1.2.2. bool
オーバーロード |
|
パラメータ化 | T はスカラー型です。
|
説明 |
bool値を構築します。
もし |
17.1.2.3. f16
オーバーロード |
|
パラメータ化 | T はスカラー型です
|
説明 |
f16値を構築します。
もし |
17.1.2.4. f32
オーバーロード |
|
パラメータ化 | T は具体的スカラー型です
|
説明 |
f32値を構築します。
もし |
17.1.2.5. i32
オーバーロード |
|
パラメータ化 | T はスカラー型です
|
説明 |
i32値を構築します。
もし |
17.1.2.6. mat2x2
オーバーロード |
|
パラメータ化 | T はf16またはf32S はAbstractFloat、f16、またはf32です
|
説明 |
2x2列主導の行列のコンストラクタです。
|
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32です
|
説明 | 列ベクトルから2x2列主導行列を構築します。 |
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32です
|
説明 |
要素から2x2列主導行列を構築します。
mat2x2(vec2(e1,e2), vec2(e3,e4))と同じです。 |
17.1.2.7. mat2x3
オーバーロード |
|
パラメータ化 | T は f16 または f32S は AbstractFloat、f16、または f32
|
説明 |
2x3 列主導 行列のコンストラクタ。
|
オーバーロード |
|
パラメータ化 | T は AbstractFloat、f16、または f32
|
説明 | 列ベクトルから 2x3 列主導 行列を構築します。 |
オーバーロード |
|
パラメータ化 | T は AbstractFloat、f16、または f32
|
説明 |
要素から 2x3 列主導 行列を構築します。
mat2x3(vec3(e1,e2,e3), vec3(e4,e5,e6)) と同じです。 |
17.1.2.8. mat2x4
オーバーロード |
|
パラメータ化 | T は f16 または f32S は AbstractFloat、f16、または f32
|
説明 |
2x4 列主導 行列のコンストラクタ。
|
オーバーロード |
|
パラメータ化 | T は AbstractFloat、f16、または f32
|
説明 | 列ベクトルから 2x4 列主導 行列を構築します。 |
オーバーロード |
|
パラメータ化 | T は AbstractFloat、f16、または f32
|
説明 |
要素から 2x4 列主導 行列を構築します。
mat2x4(vec4(e1,e2,e3,e4), vec4(e5,e6,e7,e8)) と同じです。 |
17.1.2.9. mat3x2
オーバーロード |
|
パラメータ化 | T は f16 または f32S は AbstractFloat、f16、または f32
|
説明 |
3x2 列主導 行列のコンストラクタ。
|
オーバーロード |
|
パラメータ化 | T は AbstractFloat、f16、または f32
|
説明 | 列ベクトルから 3x2 列主導 行列を構築します。 |
オーバーロード |
|
パラメータ化 | T は AbstractFloat、f16、または f32
|
説明 |
要素から 3x2 列主導 行列を構築します。
mat3x2(vec2(e1,e2), vec2(e3,e4), vec2(e5,e6)) と同じです。 |
17.1.2.10.
mat3x3
オーバーロード |
|
パラメータ化 | T はf16またはf32S はAbstractFloat、f16、またはf32
|
説明 |
3x3列主導行列のコンストラクタ。
|
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 | 列ベクトルから3x3列主導行列を構築します。 |
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 |
要素から3x3列主導行列を構築します。
mat3x3(vec3(e1,e2,e3), vec3(e4,e5,e6), vec3(e7,e8,e9))と同じです。 |
17.1.2.11.
mat3x4
オーバーロード |
|
パラメータ化 | T はf16またはf32S はAbstractFloat、f16、またはf32
|
説明 |
3x4列主導行列のコンストラクタ。
|
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 | 列ベクトルから3x4列主導行列を構築します。 |
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 |
要素から3x4列主導行列を構築します。
mat3x4(vec4(e1,e2,e3,e4), vec4(e5,e6,e7,e8), vec4(e9,e10,e11,e12))と同じです。 |
17.1.2.12.
mat4x2
オーバーロード |
|
パラメータ化 | T はf16またはf32S はAbstractFloat、f16、またはf32
|
説明 |
4x2列主導行列のコンストラクタ。
|
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 | 列ベクトルから4x2列主導行列を構築します。 |
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 |
要素から4x2列主導行列を構築します。
mat4x2(vec2(e1,e2), vec2(e3,e4), vec2(e5,e6), vec2(e7,e8))と同じです。 |
17.1.2.13.
mat4x3
オーバーロード |
|
パラメータ化 | T はf16またはf32S はAbstractFloat、f16、またはf32
|
説明 |
4x3列主導行列のコンストラクタ。
|
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 | 列ベクトルから4x3列主導行列を構築します。 |
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 |
要素から4x3列主導行列を構築します。
mat4x3(vec3(e1,e2,e3), vec3(e4,e5,e6), vec3(e7,e8,e9), vec3(e10,e11,e12))と同じです。 |
17.1.2.14.
mat4x4
オーバーロード |
|
パラメータ化 | T はf16またはf32S はAbstractFloat、f16、またはf32
|
説明 |
4x4列主導行列のコンストラクタ。
|
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 | 列ベクトルから4x4列主導行列を構築します。 |
オーバーロード |
|
パラメータ化 | T はAbstractFloat、f16、またはf32
|
説明 |
要素から4x4列主導行列を構築します。
mat4x4(vec4(e1,e2,e3,e4), vec4(e5,e6,e7,e8), vec4(e9,e10,e11,e12), vec4(e13,e14,e15,e16))と同じです。 |
17.1.2.15. 構造体
オーバーロード |
|
パラメータ化 | S は、メンバーの型がT1 ...TN である構築可能な構造体型です。
|
説明 | メンバーから型S の構造体を構築します。
|
17.1.2.16. u32
オーバーロード |
|
パラメータ化 | T はスカラー型です
|
説明 |
u32値を構築します。
もし |
注: AbstractIntからのオーバーロードは、 |
17.1.2.17. vec2
オーバーロード |
|
パラメータ化 | T は具体的なスカラー型S はスカラー型
|
説明 | 2成分のベクトルをe を両成分として構築します。
|
オーバーロード |
|
パラメータ化 | T は具体的なスカラー型S はスカラー型
|
説明 |
成分ごとの2成分ベクトルをe.x とe.y を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの2成分ベクトルをe1 とe2 を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はAbstractInt
|
説明 | vec2(0,0) の値を返します。
|
17.1.2.18. vec3
オーバーロード |
|
パラメータ化 | T は具体的なスカラー型S はスカラー型
|
説明 | 3成分ベクトルをe をすべての成分として構築します。
|
オーバーロード |
|
パラメータ化 | T は具体的なスカラー型S はスカラー型
|
説明 |
成分ごとの3成分ベクトルをe.x 、e.y 、e.z を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの3成分ベクトルをe1 、e2 、e3 を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの3成分ベクトルをv1.x 、v1.y 、e1 を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの3成分ベクトルをe1 、v1.x 、v1.y を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はAbstractInt
|
説明 | vec3(0,0,0) の値を返します。
|
17.1.2.19. vec4
オーバーロード |
|
パラメータ化 | T は具体的なスカラー型S はスカラー型
|
説明 | 4成分ベクトルをe をすべての成分として構築します。
|
オーバーロード |
|
パラメータ化 | T は具体的なスカラー型S はスカラー型
|
説明 |
成分ごとの4成分ベクトルをe.x 、e.y 、e.z 、e.w を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの4成分ベクトルをe1 、e2 、e3 、e4 を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの4成分ベクトルをe1 、v1.x 、v1.y 、e2 を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの4成分ベクトルをe1 、e2 、v1.x 、v1.y を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの4成分ベクトルをv1.x 、v1.y 、v2.x 、v2.y を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの4成分ベクトルをv1.x 、v1.y 、e1 、e2 を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はスカラー型
|
説明 | 成分ごとの4成分ベクトルをv1.x 、v1.y 、v1.z 、e1 を成分として構築します。
|
オーバーロード |
|
パラメータ化 | T はAbstractInt
|
説明 | vec4(0,0,0,0) の値を返します。
|
17.2. ビット再解釈組み込み関数
17.2.1. bitcast
bitcast
組み込み関数は、ある型の値のビット表現を別の型の値として再解釈するために使用されます。
内部レイアウトの規則は § 14.4.4 値の内部レイアウトで説明されています。
オーバーロード |
|
パラメータ化 | T は 具体的な 数値スカラー または 具体的な 数値ベクター
|
説明 | 恒等変換。 要素ごと、 T が ベクターの場合。結果は e です。
|
オーバーロード |
|
パラメータ化 | S は i32, u32, または f32T は S ではなく、i32, u32, または f32
|
説明 | ビットを T として再解釈します。結果は e のビットを T 値として再解釈したものです。
|
オーバーロード |
|
パラメータ化 | S は i32, u32, または f32T は S ではなく、i32, u32, または f32
|
説明 | 要素ごとにビットを
T として再解釈します。結果は e のビットを vecN<T> 値として再解釈したものです。
|
オーバーロード |
|
パラメータ化 | |
説明 |
e が u32
として表現可能な場合は恒等操作となり、
そうでない場合は シェーダー生成エラーを生成します。
つまり、u32(e) と同じ結果を生成します。
要素ごと、 |
オーバーロード |
|
パラメータ化 | T は i32, u32, または f32
|
説明 | 要素ごとにビットを
T として再解釈します。結果は e の32ビットを内部レイアウト規則に従い T 値として再解釈したものです。
|
オーバーロード |
|
パラメータ化 | T は i32, u32, または f32 |
説明 | 要素ごとにビットを
T として再解釈します。結果は e の64ビットを内部レイアウト規則に従い T 値として再解釈したものです。
|
オーバーロード |
|
パラメータ化 | T は i32, u32, または f32
|
説明 | 要素ごとにビットを f16
として再解釈します。 結果は e の32ビットを内部レイアウト規則に従い f16 値として再解釈したものです。
|
オーバーロード |
|
パラメータ化 | T は i32, u32, または f32
|
説明 | 要素ごとにビットを
vec2<f16> として再解釈します。結果は e の64ビットを内部レイアウト規則に従い f16 値として再解釈したものです。
|
17.3. 論理組み込み関数
17.3.1. all
オーバーロード |
|
説明 | e の各要素が true の場合に true を返します。
|
オーバーロード |
|
説明 | e を返します。
|
17.3.2. any
オーバーロード |
|
説明 | e のいずれかの要素が true の場合に true を返します。
|
オーバーロード |
|
説明 | e を返します。
|
17.3.3. select
オーバーロード |
|
パラメータ化 | T は スカラー または ベクター
|
説明 | cond が true の場合は t 、そうでない場合は f を返します。
|
オーバーロード |
|
パラメータ化 | T は スカラー
|
説明 | 要素ごとに選択します。結果の要素 i は
select(f[i], t[i], cond[i]) として評価されます。
|
17.4. 配列組み込み関数
17.4.1.
arrayLength
オーバーロード |
|
パラメータ化 | E は 実行時サイズ配列の要素型,アクセスモード AM は read または read_write
|
説明 |
NRuntime、すなわち実行時サイズ配列内の要素数を返します。
詳細は § 13.3.4 バッファバインディングによる実行時サイズ配列要素数の決定 を参照してください。 |
struct PointLight { position: vec3f, color : vec3f, } struct LightStorage { pointCount : u32, point : array< PointLight > , } @group ( 0 ) @binding ( 1 ) var < storage> lights : LightStorage ; fn num_point_lights () -> u32{ return arrayLength ( & lights . point ); }
17.5. 数値組み込み関数
17.5.1. abs
オーバーロード |
|
パラメータ化 | S は AbstractInt, AbstractFloat, i32, u32, f32, または f16 T は S または vecN<S> |
説明 |
e の絶対値。
要素ごと、T がベクターの場合。
|
17.5.2. acos
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
e の逆余弦(cos-1)の主値(ラジアン)を返します。すなわち、 cos (x ) = e となる 0 ≤ x ≤ π の
x を近似します。
要素ごと、 |
スカラー定義域 | 区間 [−1, 1] |
17.5.3. acosh
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
x の逆双曲線余弦(cosh-1)を双曲線角として返します。すなわち、 cosh (a ) = x となる 0 ≤ a ≤ +∞ の a を近似します。
要素ごと、 |
スカラー定義域 | 区間 [1, +∞] |
17.5.4. asin
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
e の逆正弦(sin-1)の主値(ラジアン)を返します。すなわち、 sin (x ) = e となる -π/2 ≤ x ≤ π/2 の
x を近似します。
要素ごと、 |
スカラー定義域 | 区間 [−1, 1] |
17.5.5. asinh
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
y の逆双曲線正弦(sinh-1)を双曲線角として返します。すなわち、 sinh (y ) = a となる a を近似します。
要素ごと、 |
17.5.6. atan
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
e の逆正接(tan-1)の主値(ラジアン)を返します。すなわち、 tan (x ) = e となる − π/2 ≤ x ≤ π/2 の
x を近似します。
要素ごと、 |
17.5.7. atanh
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
t の逆双曲線正接(tanh-1)を双曲線角として返します。すなわち、 tanh (a ) = t となる a を近似します。
要素ごと、 |
スカラー定義域 | 区間 [−1, 1] |
17.5.8. atan2
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
角度(ラジアン単位)を区間 [-π, π] で返し、その正接が
y ÷x となります。
結果の象限は
注: 結果の誤差は無制限です:
要素ごと、 |
17.5.9. ceil
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | 天井(ceil)を
e に対して返します。
要素ごと、T がベクターの場合。
|
17.5.10. clamp
オーバーロード |
|
パラメータ化 | S は AbstractInt, AbstractFloat, i32, u32, f32, または f16 T は S または vecN<S> |
説明 |
e の値を範囲内に制限します。
要素ごと、
|
17.5.11. cos
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e (ラジアン単位)の余弦を返します。
要素ごと、T がベクターの場合。
|
スカラー定義域 | 区間 (−∞, +∞) |
17.5.12. cosh
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
a (双曲線角)の双曲線余弦を返します。
純粋な数学関数 (ea +
e−a)÷2 を近似しますが、必ずしもその方法で計算されるわけではありません。
要素ごと、 |
17.5.13.
countLeadingZeros
オーバーロード |
|
パラメータ化 | T は i32, u32, vecN<i32>, または vecN<u32>
|
説明 | T がスカラー型の場合、e の最上位ビットから連続する 0 のビット数。要素ごと、 T がベクターの場合。一部の言語では "clz" とも呼ばれます。 |
17.5.14.
countOneBits
オーバーロード |
|
パラメータ化 | T は i32, u32, vecN<i32>, または vecN<u32>
|
説明 | e の表現に含まれる 1 のビット数。"population count" とも呼ばれます。 要素ごと、 T がベクターの場合。
|
17.5.15.
countTrailingZeros
オーバーロード |
|
パラメータ化 | T は i32, u32, vecN<i32>, または vecN<u32>
|
説明 | T がスカラー型の場合、e の最下位ビットから連続する 0 のビット数。要素ごと、 T がベクターの場合。一部の言語では "ctz" とも呼ばれます。 |
17.5.16. cross
オーバーロード |
|
パラメータ化 | T は AbstractFloat, f32, または f16
|
説明 | e1 と e2 の外積を返します。
|
定義域 |
線形項から暗黙的に決定される可能な実装:
|
17.5.17. degrees
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | ラジアンから度への変換(e1 × 180 ÷ π の近似)。
要素ごと、T がベクターの場合
|
17.5.18.
determinant
オーバーロード |
|
パラメータ化 | T は AbstractFloat, f32, または f16
|
説明 | e の行列式を返します。
|
定義域 | 標準的な数学的な行列式定義における線形項から暗黙的に決定。 |
17.5.19. distance
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
e1 と e2 の距離を返します(例:
length(e1 - e2) )。
定義域は、
減算 e1−e2 が有効なすべてのベクター (e1,e2) です。
すなわち、ある要素 |
17.5.20. dot
オーバーロード |
|
パラメータ化 | T は AbstractInt, AbstractFloat, i32, u32, f32, または f16
|
説明 | e1 と e2 のドット積(内積)を返します。
|
定義域 | 線形項の和(e1[i] × e2[i])から暗黙的に決定されます。 |
17.5.21.
dot4U8Packed
オーバーロード |
|
説明 | e1 および e2 は4つの8ビット符号なし整数成分を持つベクターとして解釈されます。
この2つのベクターの符号なし整数ドット積を返します。
|
17.5.22.
dot4I8Packed
オーバーロード |
|
説明 | e1 および e2 は4つの8ビット符号付き整数成分を持つベクターとして解釈されます。
この2つのベクターの符号付き整数ドット積を返します。各成分は乗算前にi32へ符号拡張され、その後加算はWGSLのi32で行われます(加算はオーバーフローしません。結果は数学的に-65024から65536の範囲に収まるため、i32で十分表現可能です)。
|
17.5.23. exp
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e1 の自然指数関数(例:e e1 )を返します。
要素ごと、T がベクターの場合。
|
17.5.24. exp2
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e の2乗(例:2 e )を返します。
要素ごと、T がベクターの場合。
|
17.5.25.
extractBits
(signed)
オーバーロード |
|
パラメータ化 | T は i32 または vecN<i32>
|
説明 |
整数からビットを読み取り、符号拡張を行います。
T がベクターの場合。
|
17.5.26.
extractBits
(unsigned)
オーバーロード |
|
パラメータ化 | T は u32 または vecN<u32>
|
説明 |
整数からビットを読み取り、符号拡張はしません。
T がベクターの場合。
|
17.5.27.
faceForward
オーバーロード |
|
パラメータ化 | T は vecN<AbstractFloat>, vecN<f32>, または vecN<f16>
|
説明 | dot(e2, e3) が負の場合は e1 を、そうでなければ -e1 を返します。
|
定義域 | dot(e2,e3) 演算に由来する定義域制約:線形項(e2[i] ×
e3[i])の和から暗黙的に決定されます。
|
17.5.28. firstLeadingBit
(signed)
オーバーロード |
|
パラメータ化 | T は i32 または vecN<i32>
|
説明 |
スカラー T の場合、結果は以下の通りです:
要素ごと、 |
注:符号付き整数は2の補数表現であるため、 符号ビットは最上位ビット位置に現れます。 |
17.5.29. firstLeadingBit
(unsigned)
オーバーロード |
|
パラメータ化 | T は u32 または vecN<u32>
|
説明 |
スカラー T の場合、結果は以下の通りです:
T がベクターの場合。
|
17.5.30.
firstTrailingBit
オーバーロード |
|
パラメータ化 | T は i32, u32, vecN<i32>, または vecN<u32>
|
説明 |
スカラー T の場合、結果は以下の通りです:
T がベクターの場合。
|
17.5.31. floor
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e の床(floor)を返します。
要素ごと、T がベクターの場合。
|
17.5.32. fma
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
e1 * e2 + e3 を返します。
要素ごと、T がベクターの場合。
注: 注:
IEEE-754
の |
定義域 | 線形項(e2 × e2 + e3)から暗黙的に決定されます。 |
17.5.33. fract
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e - floor(e) として計算される e の小数部分を返します。要素ごと、 T がベクターの場合。
|
注: 有効な結果は閉区間 [0, 1.0] に収まります。
例えば |
17.5.34. frexp
オーバーロード |
|
パラメータ化 | T は f32
|
説明 |
e を小数部と指数部に分割します。
注: |
注: |
オーバーロード |
|
パラメータ化 | T は f16
|
説明 |
e を小数部と指数部に分割します。
注: |
注: |
オーバーロード |
|
パラメータ化 | T は AbstractFloat
|
説明 |
e を小数部と指数部に分割します。
注: AbstractFloat 式で無限大や NaN になるとシェーダー生成エラーとなります。
注: |
注: |
オーバーロード |
|
パラメータ化 | T は vecN<f32>
|
説明 |
e の各要素 ei を小数部と指数部に分割します。
注: |
注: |
オーバーロード |
|
パラメータ化 | T は vecN<f16>
|
説明 |
e の各要素 ei を小数部と指数部に分割します。
注: |
注: |
オーバーロード |
|
パラメータ化 | T は vecN<AbstractFloat>
|
説明 |
e の各要素 ei を小数部と指数部に分割します。
注: AbstractFloat 式で無限大や NaN になるとシェーダー生成エラーとなります。
注: |
注:
|
17.5.35.
insertBits
オーバーロード |
|
パラメータ化 | T は i32, u32, vecN<i32>, または vecN<u32>
|
説明 |
整数のビットを設定します。
T がベクターの場合。
|
17.5.36.
inverseSqrt
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | sqrt(e) の逆数を返します。
要素ごと、T がベクターの場合。
|
スカラー定義域 | 区間 [0, +∞] |
17.5.37. ldexp
オーバーロード |
|
パラメータ化 |
S は AbstractFloat, f32, または f16 T は S または vecN<S> I は AbstractInt, i32, vecN<AbstractInt>, または vecN<i32>I がベクター型の場合に限り T もベクター型T が抽象型の場合、I も抽象型でなければならず、その逆も同様
注: いずれかのパラメータが具体型の場合、 もう一方のパラメータは自動変換によって 具体型(該当する場合)となり、結果も 具体型となります。 |
説明 |
e1 * 2 e2 を返します。ただし:
ここで bias は浮動小数点フォーマットの指数バイアス:
x = ldexp(frexp(x).fract, frexp(x).exp) 要素ごと、 注: |
17.5.38. length
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
e の長さを返します。T がスカラーの場合は絶対値。T がベクター型の場合は sqrt(e[0] 2
+ e[1] 2 + ...) で評価。
注: スカラーの場合 |
17.5.39. log
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e の自然対数を返します。
要素ごと、T がベクターの場合。
|
スカラー定義域 | 区間 [0, +∞] |
17.5.40. log2
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e の底2の対数を返します。
要素ごと、T がベクターの場合。
|
スカラー定義域 | 区間 [0, +∞] |
17.5.41.
max
オーバーロード |
|
パラメータ化 | S は AbstractInt, AbstractFloat, i32, u32, f32, または f16 T は S または vecN<S> |
説明 |
e1 が e2 より小さい場合は e2 を、そうでなければ e1 を返します。
要素ごと、T がベクターの場合。
|
17.5.42.
min
オーバーロード |
|
パラメータ化 | S は AbstractInt, AbstractFloat, i32, u32, f32, または f16 T は S または vecN<S> |
説明 |
e2 が e1 より小さい場合は e2 を、そうでなければ e1 を返します。
要素ごと、T がベクターの場合。
|
17.5.43. mix
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e1 と e2 の線形合成(例:e1 * (T(1) - e3) + e2 * e3 )を返します。
要素ごと、T がベクターの場合。
|
定義域 | 線形項(e1[i] × (1 − e3[i]) + e2[i] × e3[i]、 e2[i] × e2[i] + e3[i])から暗黙的に決定されます。 |
オーバーロード |
|
パラメータ化 | T は AbstractFloat, f32, または f16T2 は vecN<T>
|
説明 | e1 と e2 の要素ごとの線形合成を、スカラー合成係数 e3 で各要素に適用します。mix(e1, e2, T2(e3)) と同じです。
|
定義域 | 線形項(e1[i] × (1 − e3) + e2[i] × e3)から暗黙的に決定されます。 |
17.5.44. modf
オーバーロード |
|
パラメータ化 | T は f32
|
説明 |
e を小数部と整数部に分割します。
整数部は trunc(
|
注: |
オーバーロード |
|
パラメータ化 | T は f16
|
説明 |
e を小数部と整数部に分割します。
整数部は trunc(
|
注: |
オーバーロード |
|
パラメータ化 | T は AbstractFloat
|
説明 |
e を小数部と整数部に分割します。
整数部は trunc(
|
注: |
オーバーロード |
|
パラメータ化 | T は vecN<f32>
|
説明 |
e の各要素を小数部と整数部に分割します。
整数部および小数部の
|
注: |
オーバーロード |
|
パラメータ化 | T は vecN<f16>
|
説明 |
e の各要素を小数部と整数部に分割します。
整数部および小数部の
|
注: |
オーバーロード |
|
パラメータ化 | T は vecN<AbstractFloat>
|
説明 |
e の各要素を小数部と整数部に分割します。
整数部および小数部の
|
注:
|
17.5.45.
normalize
オーバーロード |
|
パラメータ化 | T は AbstractFloat, f32, または f16
|
説明 |
e と同じ方向の単位ベクトルを返します。
定義域はゼロベクトル以外のすべてのベクトルです。 |
17.5.46. pow
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e1 の e2 乗を返します。
要素ごと、T がベクターの場合。
|
スカラー定義域 |
拡張実数
(x,y) の全ての組み合わせ。ただし:
結果が |
17.5.47.
quantizeToF16
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
32ビット浮動小数点値 e を、
IEEE-754 の
binary16
値に変換し、さらに
IEEE-754 binary32 値に戻したかのように量子化します。
中間の binary16 値はゼロクリアされる場合があります。すなわち、 中間の binary16 値が非正規化の場合、最終結果がゼロになる場合があります。 § 15.7.6 浮動小数点変換も参照。 要素ごと、 |
注: vec2<f32> の場合は
|
17.5.48. radians
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | 度からラジアンへの変換(e1 × π ÷ 180 の近似)。
要素ごと、T がベクターの場合
|
17.5.49. reflect
オーバーロード |
|
パラメータ化 | T は vecN<AbstractFloat>, vecN<f32>, または vecN<f16>
|
説明 | 入射ベクトル e1 と表面の向き e2 について、
反射方向 e1 - 2 * dot(e2, e1) * e2 を返します。
|
17.5.50. refract
オーバーロード |
|
パラメータ化 | T は vecN<I>I は AbstractFloat, f32, または f16
|
説明 | 入射ベクトル e1 、表面法線 e2 、屈折率の比 e3 について、
k = 1.0 - e3 * e3 * (1.0 - dot(e2, e1) * dot(e2, e1)) とする。
k < 0.0 の場合は屈折ベクトル 0.0 を返し、それ以外の場合は
屈折ベクトル e3 * e1 - (e3 * dot(e2, e1) + sqrt(k)) * e2 を返します。
入射ベクトル e1 と法線 e2 はスネルの法則に従い正規化されていることが望ましく、
そうでない場合、結果は物理的な振る舞いと一致しない可能性があります。
|
17.5.51.
reverseBits
オーバーロード |
|
パラメータ化 | T は i32, u32, vecN<i32>, または vecN<u32>
|
説明 | e のビットを反転します:結果のビット位置 k は e のビット位置 31 -k
と等しくなります。要素ごと、 T がベクターの場合。
|
17.5.52. round
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | 結果は e に最も近い整数 k (浮動小数点値)になります。e が整数 k と k + 1 のちょうど中間の場合は、
k が偶数なら k を、奇数なら k + 1 を返します。要素ごと、 T がベクターの場合。
|
17.5.53.
saturate
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | clamp(e, 0.0, 1.0) を返します。
要素ごと、T がベクターの場合。
|
17.5.54. sign
オーバーロード |
|
パラメータ化 | S は AbstractInt, AbstractFloat, i32, f32, または f16 T は S または vecN<S> |
説明 |
結果は以下の通りです:
要素ごと、 |
17.5.55. sin
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e (ラジアン単位)の正弦を返します。
要素ごと、T がベクターの場合。
|
スカラー定義域 | 区間 (−∞, +∞) |
17.5.56. sinh
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
a (双曲線角)の双曲線正弦を返します。
純粋な数学関数
(ea − e−a)÷2 を近似しますが、必ずしもその方法で計算されるわけではありません。
要素ごと、 |
17.5.57.
smoothstep
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
0 から 1 へのスムーズなエルミート補間を返します。
要素ごと、T がベクターの場合。
スカラー 定性的には:
|
17.5.58. sqrt
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e の平方根を返します。
要素ごと、T がベクターの場合。
|
スカラー定義域 | 区間 [0, +∞] |
17.5.59. step
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | edge ≤ x の場合は 1.0、そうでなければ 0.0 を返します。
要素ごと、T がベクターの場合。
|
17.5.60. tan
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | e (ラジアン単位)の正接を返します。
要素ごと、T がベクターの場合。
|
スカラー定義域 | 区間 (−∞, +∞) |
17.5.61. tanh
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 |
a (双曲線角)の双曲線正接を返します。
純粋な数学関数
(ea − e−a) ÷ (ea +
e−a)
を近似しますが、必ずしもその方法で計算されるわけではありません。
要素ごと、 |
17.5.62.
transpose
オーバーロード |
|
パラメータ化 | T は AbstractFloat, f32, または f16
|
説明 | e の転置行列を返します。
|
17.5.63. trunc
オーバーロード |
|
パラメータ化 | S は AbstractFloat, f32, または f16 T は S または vecN<S> |
説明 | truncate(e )、すなわち絶対値が e
の絶対値以下となる最も近い整数値を返します。
要素ごと、T がベクターの場合。
|
17.6. 微分組み込み関数
§ 15.6.2 微分 を参照。
これらの関数の呼び出し:
-
発動:一様性解析で 呼び出しが一様な制御フローであることが証明できない場合、 derivative_uniformity 診断を発生させます。
17.6.1. dpdx
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
ウィンドウ座標 x に関する e の偏微分。
結果は dpdxFine(e) または dpdxCoarse(e) のいずれかと同じです。
|
17.6.2. dpdxCoarse
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
ウィンドウ座標 x に関する e の偏微分(局所差分を利用)。
dpdxFine(e) よりも一意の位置が少なくなる場合があります。
|
17.6.3. dpdxFine
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
ウィンドウ座標 x に関する e の偏微分を返します。
|
17.6.4. dpdy
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
ウィンドウ座標 y に関する e の偏微分。
結果は dpdyFine(e) または dpdyCoarse(e) のいずれかと同じです。
|
17.6.5. dpdyCoarse
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
ウィンドウ座標 y に関する e の偏微分(局所差分を利用)。
dpdyFine(e) よりも一意の位置が少なくなる場合があります。
|
17.6.6. dpdyFine
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
ウィンドウ座標 y に関する e の偏微分を返します。
|
17.6.7. fwidth
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
abs(dpdx(e)) + abs(dpdy(e)) を返します。
|
17.6.8.
fwidthCoarse
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
abs(dpdxCoarse(e)) + abs(dpdyCoarse(e)) を返します。
|
17.6.9. fwidthFine
オーバーロード |
|
パラメータ化 | T は f32 または vecN<f32>
|
説明 |
abs(dpdxFine(e)) + abs(dpdyFine(e)) を返します。
|
17.7. テクスチャ組み込み関数
パラメータ値は、各テクスチャ型に対して有効である必要があります(必須)。
17.7.1. textureDimensions
テクスチャまたはテクスチャのミップレベルの次元(テクセル数)を返します。
パラメータ化 | オーバーロード |
---|---|
ST は i32, u32, または f32 F は テクセルフォーマット A は アクセスモード T は texture_1d<ST> または texture_storage_1d<F,A>
|
|
ST は i32, u32, または f32 T は texture_1d<ST>
|
|
ST は i32, u32, または f32 F は テクセルフォーマット A は アクセスモード T は texture_2d<ST> , texture_2d_array<ST> ,
texture_cube<ST> ,
texture_cube_array<ST> , texture_multisampled_2d<ST> ,
texture_depth_2d , texture_depth_2d_array ,
texture_depth_cube ,
texture_depth_cube_array , texture_depth_multisampled_2d ,
texture_storage_2d<F,A> , texture_storage_2d_array<F,A> ,
または texture_external
|
|
ST は i32, u32, または f32 T は texture_2d<ST> , texture_2d_array<ST> ,
texture_cube<ST> ,
texture_cube_array<ST> , texture_depth_2d ,
texture_depth_2d_array ,
texture_depth_cube , または texture_depth_cube_array
|
|
ST は i32, u32, または f32 F は テクセルフォーマット A は アクセスモード T は texture_3d<ST> または texture_storage_3d<F,A>
|
|
ST は i32, u32, または f32 T は texture_3d<ST>
|
|
パラメータ:
t
| サンプルテクスチャ型、 マルチサンプルテクスチャ型、デプステクスチャ型、 ストレージテクスチャ型、または 外部テクスチャ型 のいずれかのテクスチャ。 |
level
|
ミップレベル。レベル0がテクスチャのフルサイズです。 省略時はレベル0の次元を返します。 |
戻り値:
テクスチャの座標次元を返します。
つまり、戻り値は論理テクセルアドレスの座標の整数範囲を示し、 ミップレベル数、配列サイズ、サンプル数は含みません。
キューブ型テクスチャの場合、各面の次元を返します。 キューブ面は正方形なので、戻り値の x, y 成分は等しくなります。
level
が範囲 [0, textureNumLevels(t))
の外の場合、戻り値型の不定値
を返す場合があります。
17.7.2. textureGather
テクスチャギャザー 操作は2D、2D配列、キューブ、またはキューブ配列テクスチャから読み込み、 以下のように4成分のベクターを計算します:
-
線形フィルタリングによるサンプリング操作で使用される4つのテクセルを、ミップレベル0から選びます:
-
指定した座標、配列インデックス(存在する場合)、およびオフセット(存在する場合)を使用します。
-
テクセルは隣接しており、テクスチャ空間座標(u,v)で見ると正方形を形成します。
-
テクスチャ端、キューブ面端、またはキューブ角のテクセルが選ばれた場合、通常のテクスチャサンプリングと同様に扱われます。
-
-
各テクセルごとに1つのチャンネルを読み取り、スカラー値に変換します。
-
非デプステクスチャの場合、ゼロ基準の
component
パラメータで使用するチャンネルを指定します。-
テクスチャフォーマットが指定チャンネルをサポートしている、すなわち
component
チャンネル数より多い場合:-
テクセル値が
v
の場合、スカラー値v[component]
を返します。
-
-
それ以外の場合:
-
component
が1または2の場合は0.0を返します。 -
component
が3(アルファチャンネル)の場合は1.0を返します。
-
-
-
デプステクスチャの場合は、テクセル値を返します。(デプステクスチャは1チャンネルのみ。)
-
-
前のステップで得られたスカラーを、テクセルの相対座標に応じて以下のように4成分ベクターに並べて返します:
-
結果成分 相対テクセル座標 x (umin,vmax) y (umax,vmax) z (umax,vmin) w (umin,vmin)
-
4つのテクセルは、WebGPU サンプラー記述子に記載されたサンプリング領域を形成します。
パラメータ化 | オーバーロード |
---|---|
Cはi32またはu32 STはi32、u32、f32 |
|
Cはi32またはu32 STはi32、u32、f32 |
|
Cはi32またはu32 Aはi32またはu32 STはi32、u32、f32 |
|
Cはi32またはu32 Aはi32またはu32 STはi32、u32、f32 |
|
Cはi32またはu32 STはi32、u32、f32 |
|
Cはi32またはu32 Aはi32またはu32 STはi32、u32、f32 |
|
| |
| |
| |
Aはi32またはu32 |
|
Aはi32またはu32 |
|
Aはi32またはu32 |
|
パラメータ:
component
|
非デプステクスチャにのみ適用されます。
選択されたテクセルから読み取るチャンネルのインデックス。 指定された場合、 component 式は必須で定数式(例:1 )でなければなりません。値は0以上3以下である必要があります。 この範囲外の値はシェーダー生成エラーになります。 |
t
| 読み込み対象のサンプルテクスチャ型またはデプステクスチャ型。 |
s
| サンプラー型。 |
coords
| テクスチャ座標。 |
array_index
|
0始まりのテクスチャ配列インデックス。 この値は必ず [0, textureNumLayers(t) - 1] の範囲にクランプされます。
|
offset
|
サンプリング前に非正規化テクスチャ座標に適用されるオプションのテクセルオフセット。
このオフセットは、テクスチャラッピングモード適用前に適用されます。offset 式は必須で定数式(例:vec2<i32>(1, 2) )でなければなりません。各 offset 成分は必須で-8 以上7 以下である必要があります。この範囲外はシェーダー生成エラーです。
|
戻り値:
選択されたテクセルから指定チャンネルの成分を抜き出して並べた4成分ベクターを返します(上記参照)。
@group ( 0 ) @binding ( 0 ) var t : texture_2d< f32> ; @group ( 0 ) @binding ( 1 ) var dt : texture_depth_2d; @group ( 0 ) @binding ( 2 ) var s : sampler; fn gather_x_components ( c : vec2< f32> ) -> vec4< f32> { return textureGather ( 0 , t , s , c ); } fn gather_y_components ( c : vec2< f32> ) -> vec4< f32> { return textureGather ( 1 , t , s , c ); } fn gather_z_components ( c : vec2< f32> ) -> vec4< f32> { return textureGather ( 2 , t , s , c ); } fn gather_depth_components ( c : vec2< f32> ) -> vec4< f32> { return textureGather ( dt , s , c ); }
17.7.3.
textureGatherCompare
テクスチャギャザー比較操作は、デプステクスチャ内の4つのテクセルについて深度比較を行い、結果を1つのベクターにまとめて返します:
-
線形フィルタリングを用いた深度サンプリング操作で使用される4つのテクセルをミップレベル0から選びます:
-
指定した座標、配列インデックス(存在する場合)、およびオフセット(存在する場合)を使用します。
-
テクセルは隣接しており、テクスチャ空間座標(u,v)で正方形を形成します。
-
テクスチャ端、キューブ面端、またはキューブ角のテクセルが選ばれた場合は、通常のテクスチャサンプリング時と同様に扱われます。
-
-
各テクセルについて深度参照値と比較を行い、比較サンプラーのパラメータに従い0.0または1.0を返します。
-
前のステップで得られた比較結果を、テクセルの相対座標に応じて下表のように4成分ベクターとして返します:
-
結果成分 相対テクセル座標 x (umin,vmax) y (umax,vmax) z (umax,vmin) w (umin,vmin)
-
パラメータ化 | オーバーロード |
---|---|
| |
| |
Aはi32またはu32 |
|
Aはi32またはu32 |
|
| |
Aはi32またはu32 |
|
パラメータ:
t
| 読み込み対象のデプステクスチャ。 |
s
| 比較サンプラー。 |
coords
| テクスチャ座標。 |
array_index
|
0始まりのテクスチャ配列インデックス。 この値は必ず [0, textureNumLayers(t) - 1] の範囲にクランプされます。
|
depth_ref
| サンプリングされた深度値と比較する参照値。 |
offset
|
サンプリング前に非正規化テクスチャ座標に適用されるオプションのテクセルオフセット。
このオフセットは、テクスチャラッピングモード適用前に適用されます。offset 式は必須で定数式(例:vec2<i32>(1, 2) )でなければなりません。各 offset 成分は必須で-8 以上7 以下である必要があります。この範囲外はシェーダー生成エラーです。
|
戻り値:
選択したテクセルの比較結果を成分とする4成分ベクター(上記参照)を返します。
@group ( 0 ) @binding ( 0 ) var dt : texture_depth_2d; @group ( 0 ) @binding ( 1 ) var s : sampler; fn gather_depth_compare ( c : vec2< f32> , depth_ref : f32) -> vec4< f32> { return textureGatherCompare ( dt , s , c , depth_ref ); }
17.7.4. textureLoad
サンプリングやフィルタリングを行わずに、テクスチャから単一のテクセルを読み取ります。
パラメータ化 | オーバーロード |
---|---|
Cはi32またはu32 Lはi32またはu32 STはi32、u32、またはf32 |
|
Cはi32またはu32 Lはi32またはu32 STはi32、u32、またはf32 |
|
Cはi32またはu32 Aはi32またはu32 Lはi32またはu32 STはi32、u32、またはf32 |
|
Cはi32またはu32 Lはi32またはu32 STはi32、u32、またはf32 |
|
Cはi32またはu32 Sはi32またはu32 STはi32、u32、またはf32 |
|
Cはi32またはu32 Lはi32またはu32 |
|
Cはi32またはu32 Aはi32またはu32 Lはi32またはu32 |
|
Cはi32またはu32 Sはi32またはu32 |
|
Cはi32またはu32 |
|
Cはi32またはu32 AMはread またはread_write CFはストレージテクセルフォーマットFに依存します。 テクセルフォーマット表で テクセルフォーマットとチャンネルフォーマットの対応を確認してください。 |
|
Cはi32またはu32 AMはread またはread_write CFはストレージテクセルフォーマットFに依存します。 テクセルフォーマット表で テクセルフォーマットとチャンネルフォーマットの対応を確認してください。 |
|
Cはi32またはu32 AMはread またはread_write Aはi32またはu32 CFはストレージテクセルフォーマットFに依存します。 テクセルフォーマット表で テクセルフォーマットとチャンネルフォーマットの対応を確認してください。 |
|
Cはi32またはu32 AMはread またはread_write CFはストレージテクセルフォーマットFに依存します。 テクセルフォーマット表で テクセルフォーマットとチャンネルフォーマットの対応を確認してください。 |
|
パラメータ:
t
| サンプルテクスチャ、 マルチサンプルテクスチャ、 デプステクスチャ、 ストレージテクスチャ、または 外部テクスチャ |
coords
| 0始まりのテクセル座標。 |
array_index
| 0始まりのテクスチャ配列インデックス。 |
level
| ミップレベル。レベル0はテクスチャのフルサイズ。 |
sample_index
| マルチサンプルテクスチャの0始まりのサンプルインデックス。 |
戻り値:
フィルタリングされていないテクセルデータ。
論理テクセルアドレスが不正な場合:
-
coords
のいずれかの要素が、対応する要素の範囲[0, textureDimensions(t, level))
外の場合 -
array_index
が[0, textureNumLayers(t))
の範囲外の場合 -
level
が[0, textureNumLevels(t))
の範囲外の場合 -
sample_index
が[0, textureNumSamples(s))
の範囲外の場合
論理テクセルアドレスが不正な場合、組み込み関数は次のいずれかを返します:
-
テクスチャ範囲内のいずれかのテクセルのデータ
-
非デプステクスチャの場合、適切な型のベクター(0,0,0,0)または(0,0,0,1)
-
デプステクスチャの場合は0.0
17.7.5. textureNumLayers
配列化テクスチャのレイヤー(要素)数を返します。
パラメータ化 | オーバーロード |
---|---|
Fはテクセルフォーマット Aはアクセスモード STはi32、u32、またはf32 Tは texture_2d_array<ST> 、texture_cube_array<ST> 、
texture_depth_2d_array 、texture_depth_cube_array 、
またはtexture_storage_2d_array<F,A>
|
|
パラメータ:
t
| サンプルテクスチャ、 デプステクスチャ、 または ストレージテクスチャの配列テクスチャ。 |
戻り値:
テクスチャがキューブベースの場合は、キューブ配列テクスチャ内のキューブ数を返します。
それ以外の場合は、配列化テクスチャ内のレイヤー(テクセルの同種グリッド)数を返します。
17.7.6. textureNumLevels
テクスチャのミップレベル数を返します。
パラメータ化 | オーバーロード |
---|---|
STはi32、u32、またはf32 Tは texture_1d<ST> 、texture_2d<ST> 、
texture_2d_array<ST> 、texture_3d<ST> 、
texture_cube<ST> 、texture_cube_array<ST> 、
texture_depth_2d 、texture_depth_2d_array 、
texture_depth_cube 、またはtexture_depth_cube_array
|
|
パラメータ:
t
| サンプルテクスチャまたはデプステクスチャ。 |
戻り値:
テクスチャのミップレベル数を返します。
17.7.7. textureNumSamples
マルチサンプルテクスチャ内のテクセルごとのサンプル数を返します。
パラメータ化 | オーバーロード |
---|---|
STはi32、u32、またはf32 Tは texture_multisampled_2d<ST>
またはtexture_depth_multisampled_2d
|
|
パラメータ:
t
| マルチサンプルテクスチャ。 |
戻り値:
サンプル数を マルチサンプルテクスチャに対して返します。
17.7.8. textureSample
テクスチャをサンプリングします。
必須:フラグメントシェーダーステージのみで使用してください。
この関数の呼び出しが一様な制御フローであることを一様性解析が証明できない場合、 derivative_uniformity 診断が発動します。
パラメータ化 | オーバーロード |
---|---|
| |
| |
| |
Aはi32またはu32 |
|
Aはi32またはu32 |
|
Tはtexture_3d<f32> またはtexture_cube<f32>
|
|
| |
Aはi32またはu32 |
|
| |
| |
Aはi32またはu32 |
|
Aはi32またはu32 |
|
| |
Aはi32またはu32 |
|
パラメータ:
t
| サンプリング対象のサンプルテクスチャまたはデプステクスチャ。 |
s
| サンプラー型。 |
coords
| サンプリングに使用するテクスチャ座標。 |
array_index
|
サンプリング対象の0始まりのテクスチャ配列インデックス。 この値は必ず [0, textureNumLayers(t) - 1] の範囲にクランプされます。
|
offset
|
サンプリング前に非正規化テクスチャ座標に適用されるオプションのテクセルオフセット。
このオフセットは、テクスチャラッピングモード適用前に適用されます。offset 式は必須で定数式(例:vec2<i32>(1, 2) )でなければなりません。各 offset 成分は必須で-8 以上7 以下である必要があります。この範囲外はシェーダー生成エラーです。
|
戻り値:
サンプリングされた値。
17.7.9. textureSampleBias
ミップレベルにバイアスをかけてテクスチャをサンプリングします。
必須:フラグメントシェーダーステージのみで使用してください。
この関数の呼び出しが一様な制御フローであることを一様性解析が証明できない場合、 derivative_uniformity 診断が発動します。
パラメータ化 | オーバーロード |
---|---|
| |
| |
Aはi32またはu32 |
|
Aはi32またはu32 |
|
Tはtexture_3d<f32> またはtexture_cube<f32>
|
|
| |
Aはi32またはu32 |
|
パラメータ:
t
| サンプリング対象のサンプルテクスチャ。 |
s
| サンプラー型。 |
coords
| サンプリングに使用するテクスチャ座標。 |
array_index
|
サンプリング対象の0始まりのテクスチャ配列インデックス。 この値は必ず [0, textureNumLayers(t) - 1] の範囲にクランプされます。
|
bias
|
サンプリング前にミップレベルへ適用するバイアス。 この値は必ず [-16.0, 15.99] の範囲にクランプされます。
|
offset
|
サンプリング前に非正規化テクスチャ座標に適用されるオプションのテクセルオフセット。
このオフセットは、テクスチャラッピングモード適用前に適用されます。offset 式は必須で定数式(例:vec2<i32>(1, 2) )でなければなりません。各 offset 成分は必須で-8 以上7 以下である必要があります。この範囲外はシェーダー生成エラーです。
|
戻り値:
サンプリングされた値。
17.7.10.
textureSampleCompare
デプステクスチャをサンプリングし、サンプリングされた深度値を参照値と比較します。
必須:フラグメントシェーダーステージのみで使用してください。
この関数の呼び出しが一様な制御フローであることを一様性解析が証明できない場合、 derivative_uniformity 診断が発動します。
パラメータ化 | オーバーロード |
---|---|
| |
| |
Aはi32またはu32 |
|
Aはi32またはu32 |
|
| |
Aはi32またはu32 |
|
パラメータ:
t
| サンプリング対象のデプステクスチャ。 |
s
| sampler_comparison型。 |
coords
| サンプリングに使用するテクスチャ座標。 |
array_index
|
サンプリング対象の0始まりのテクスチャ配列インデックス。 この値は必ず [0, textureNumLayers(t) - 1] の範囲にクランプされます。
|
depth_ref
| サンプリングされた深度値と比較する参照値。 |
offset
|
サンプリング前に非正規化テクスチャ座標に適用されるオプションのテクセルオフセット。
このオフセットは、テクスチャラッピングモード適用前に適用されます。offset 式は必須で定数式(例:vec2<i32>(1, 2) )でなければなりません。各 offset 成分は必須で-8 以上7 以下である必要があります。この範囲外はシェーダー生成エラーです。
|
戻り値:
範囲[0.0..1.0]
の値。
各サンプリングされたテクセルは参照値と比較され、sampler_comparison
で定義された比較演算子により各テクセルごとに0
または1
が返されます。
サンプラーがバイリニアフィルタリングを使う場合は、返される値はこれらの値のフィルタ平均になり、それ以外の場合は単一テクセルの比較結果が返されます。
17.7.11.
textureSampleCompareLevel
デプステクスチャをサンプリングし、サンプリングされた深度値を参照値と比較します。
パラメータ化 | オーバーロード |
---|---|
| |
| |
Aはi32またはu32 |
|
Aはi32またはu32 |
|
| |
Aはi32またはu32 |
|
パラメータ:
t
| サンプリング対象のデプステクスチャ。 |
s
| sampler_comparison型。 |
coords
| サンプリングに使用するテクスチャ座標。 |
array_index
|
サンプリング対象の0始まりのテクスチャ配列インデックス。 この値は必ず [0, textureNumLayers(t) - 1] の範囲にクランプされます。
|
depth_ref
| サンプリングされた深度値と比較する参照値。 |
offset
|
サンプリング前に非正規化テクスチャ座標に適用されるオプションのテクセルオフセット。
このオフセットは、テクスチャラッピングモード適用前に適用されます。offset 式は必須で定数式(例:vec2<i32>(1, 2) )でなければなりません。各 offset 成分は必須で-8 以上7 以下である必要があります。この範囲外はシェーダー生成エラーです。
|
戻り値:
範囲[0.0..1.0]
の値。
textureSampleCompareLevel
関数はtextureSampleCompare
と同じですが、以下の違いがあります:
-
textureSampleCompareLevel
は常にミップレベル0のテクセルをサンプリングします。-
この関数は微分を計算しません。
-
textureSampleCompareLevel
を一様な制御フローで呼び出す必要はありません。
-
-
textureSampleCompareLevel
は任意のシェーダーステージで呼び出すことができます。
17.7.12.
textureSampleGrad
明示的な勾配を使ってテクスチャをサンプリングします。
パラメータ化 | オーバーロード |
---|---|
| |
| |
Aはi32またはu32 |
|
Aはi32またはu32 |
|
Tはtexture_3d<f32> またはtexture_cube<f32>
|
|
| |
Aはi32またはu32 |
|
パラメータ:
t
| サンプリング対象のサンプルテクスチャ。 |
s
| サンプラー。 |
coords
| サンプリングに使用するテクスチャ座標。 |
array_index
|
サンプリング対象の0始まりのテクスチャ配列インデックス。 この値は必ず [0, textureNumLayers(t) - 1] の範囲にクランプされます。
|
ddx
| サンプリング位置の計算に使用されるx方向の微分ベクトル。 |
ddy
| サンプリング位置の計算に使用されるy方向の微分ベクトル。 |
offset
|
サンプリング前に非正規化テクスチャ座標に適用されるオプションのテクセルオフセット。
このオフセットは、テクスチャラッピングモード適用前に適用されます。offset 式は必須で定数式(例:vec2<i32>(1, 2) )でなければなりません。各 offset 成分は必須で-8 以上7 以下である必要があります。この範囲外はシェーダー生成エラーです。
|
戻り値:
サンプリングされた値。
17.7.13.
textureSampleLevel
明示的なミップレベルを指定してテクスチャをサンプリングします。
パラメータ化 | オーバーロード |
---|---|
| |
| |
| |
Aはi32またはu32 |
|
Aはi32またはu32 |
|
Tはtexture_3d<f32> またはtexture_cube<f32>
|
|
| |
Aはi32またはu32 |
|
Lはi32またはu32 |
|
Lはi32またはu32 |
|
Aはi32またはu32 Lはi32またはu32 |
|
Aはi32またはu32 Lはi32またはu32 |
|
Lはi32またはu32 |
|
Aはi32またはu32 Lはi32またはu32 |
|
パラメータ:
t
| サンプリング対象のサンプルテクスチャまたはデプステクスチャ。 |
s
| サンプラー型。 |
coords
| サンプリングに使用するテクスチャ座標。 |
array_index
|
サンプリング対象の0始まりのテクスチャ配列インデックス。 この値は必ず [0, textureNumLayers(t) - 1] の範囲にクランプされます。
|
level
|
ミップレベル。レベル0はテクスチャのフルサイズ。
level がf32 の場合、フォーマットがフィルタ可能なら二つのレベル間で補間されることがあります。詳細は
Texture Format
Capabilities参照。
|
offset
|
サンプリング前に非正規化テクスチャ座標に適用されるオプションのテクセルオフセット。
このオフセットは、テクスチャラッピングモード適用前に適用されます。offset 式は必須で定数式(例:vec2<i32>(1, 2) )でなければなりません。各 offset 成分は必須で-8 以上7 以下である必要があります。この範囲外はシェーダー生成エラーです。
|
戻り値:
サンプリングされた値。
17.7.14.
textureSampleBaseClampToEdge
テクスチャビューのベースレベルをサンプリングし、 テクスチャ座標は以下の通りエッジにクランプされます。
パラメータ化 | オーバーロード |
---|---|
Tはtexture_2d<f32> またはtexture_external
|
|
パラメータ:
t
| サンプリング対象のサンプルテクスチャまたは外部テクスチャ。 |
s
| サンプラー型。 |
coords
|
サンプリングに使用するテクスチャ座標。
サンプリング前に、指定した座標は必ず以下の矩形範囲にクランプされます:
ここで
注: ハーフテクセル調整により、
サンプラーの |
戻り値:
サンプリングされた値。
17.7.15. textureStore
テクスチャに単一のテクセルを書き込みます。
パラメータ化 | オーバーロード |
---|---|
Fはテクセルフォーマット Cはi32またはu32 AMはwriteまたはread_write CFはストレージテクセルフォーマットFに依存します。 テクセルフォーマット表で テクセルフォーマットとチャンネルフォーマットの対応を確認してください。 |
|
Fはテクセルフォーマット Cはi32またはu32 AMはwriteまたはread_write CFはストレージテクセルフォーマットFに依存します。 テクセルフォーマット表で テクセルフォーマットとチャンネルフォーマットの対応を確認してください。 |
|
Fはテクセルフォーマット Cはi32またはu32 AMはwriteまたはread_write Aはi32またはu32 CFはストレージテクセルフォーマットFに依存します。 テクセルフォーマット表で テクセルフォーマットとチャンネルフォーマットの対応を確認してください。 |
|
Fはテクセルフォーマット Cはi32またはu32 AMはwriteまたはread_write CFはストレージテクセルフォーマットFに依存します。 テクセルフォーマット表で テクセルフォーマットとチャンネルフォーマットの対応を確認してください。 |
|
パラメータ:
t
| 書き込み専用ストレージテクスチャまたは 読書き可能ストレージテクスチャ |
coords
|
0始まりのテクセル座標。 |
array_index
| 0始まりのテクスチャ配列インデックス。 |
value
|
新しいテクセル値。
value は逆チャンネル伝達関数で変換されます。
|
注:
論理テクセルアドレスが不正な場合:
-
coords
のいずれかの要素が、対応する要素の範囲[0, textureDimensions(t))
外の場合 -
array_index
が[0, textureNumLayers(t))
の範囲外の場合
論理テクセルアドレスが不正な場合、組み込み関数は必ず実行されません。
17.8. アトミック組み込み関数
アトミック組み込み関数は、アトミックなオブジェクトの読み取り/書き込み/読取-修正-書き込みに使用できます。これらは§ 6.2.8 アトミック型で許可された唯一の操作です。
すべてのアトミック組み込み関数はrelaxed
なメモリ順序を使用します。これは、同期と順序の保証が同じメモリ位置で動作するアトミック操作間にのみ適用されることを意味します。アトミックと非アトミックのメモリアクセス間、または異なるメモリ位置で動作するアトミックアクセス間には同期や順序の保証はありません。
アトミック組み込み関数は絶対に頂点シェーダーステージで使用してはいけません。
すべてのアトミック組み込み関数におけるatomic_ptr
パラメータのアドレス空間AS
は、必ずstorageまたはworkgroupでなければなりません。
17.8.1. atomicLoad
fn atomicLoad ( atomic_ptr : ptr< AS , atomic< T > , read_write> ) -> T
atomic_ptr
で指された値をアトミックにロードして返します。オブジェクト自体は変更しません。
17.8.2. atomicStore
fn atomicStore ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T )
v
の値をatomic_ptr
で指されたアトミックオブジェクトにアトミックに格納します。
17.8.3. アトミック読取-修正-書き込み演算・論理関数
各関数は以下の手順をアトミックに行います:
-
atomic_ptr
で指された元の値をロードする。 -
関数名から指定された操作(例: max)を値vとともに実行し、新しい値を取得する。
-
atomic_ptr
を使って新しい値を格納する。
各関数は、操作前にアトミックオブジェクトに格納されていた元の値を返します。
17.8.3.1. atomicAdd
fn atomicAdd ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T
atomic_ptr
で指されたアトミックオブジェクトにv
の値を加算し、操作前に格納されていた元の値を返します。
// すべての操作はアトミックに実行されます fn atomicAdd ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T { let old = * atomic_ptr ; * atomic_ptr = old + v ; return old ; }
17.8.3.2. atomicSub
fn atomicSub ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T
atomic_ptr
で指されたアトミックオブジェクトからv
の値を減算し、操作前に格納されていた元の値を返します。
// すべての操作はアトミックに実行されます fn atomicSub ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T { let old = * atomic_ptr ; * atomic_ptr = old - v ; return old ; }
17.8.3.3. atomicMax
fn atomicMax ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T
atomic_ptr
で指されたアトミックオブジェクトに対しv
との最大値をアトミックに計算し、操作前の元の値を返します。
// すべての操作はアトミックに実行されます fn atomicMax ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T { let old = * atomic_ptr ; * atomic_ptr = max ( old , v ); return old ; }
17.8.3.4. atomicMin
fn atomicMin ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T
atomic_ptr
で指されたアトミックオブジェクトに対しv
との最小値をアトミックに計算し、操作前の元の値を返します。
// すべての操作はアトミックに実行されます fn atomicMin ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T { let old = * atomic_ptr ; * atomic_ptr = min ( old , v ); return old ; }
17.8.3.5. atomicAnd
fn atomicAnd ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T
atomic_ptr
が指すアトミックオブジェクトに値v
とのビットごとのAND演算をアトミックに行い、操作前にアトミックオブジェクトに格納されていた元の値を返します。
// すべての操作はアトミックに実行されます fn atomicAnd ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T { let old = * atomic_ptr ; * atomic_ptr = old & v ; return old ; }
17.8.3.6. atomicOr
fn atomicOr ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T
atomic_ptr
が指すアトミックオブジェクトに値v
とのビットごとのOR演算をアトミックに行い、操作前にアトミックオブジェクトに格納されていた元の値を返します。
// すべての操作はアトミックに実行されます fn atomicOr ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T { let old = * atomic_ptr ; * atomic_ptr = old | v ; return old ; }
17.8.3.7. atomicXor
fn atomicXor ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T
atomic_ptr
が指すアトミックオブジェクトに値v
とのビットごとのXOR演算をアトミックに行い、操作前にアトミックオブジェクトに格納されていた元の値を返します。
// すべての操作はアトミックに実行されます fn atomicXor ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T { let old = * atomic_ptr ; * atomic_ptr = old ^ v ; return old ; }
17.8.4. atomicExchange
fn atomicExchange ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T
atomic_ptr
が指すアトミックオブジェクトに値v
をアトミックに格納し、操作前にアトミックオブジェクトに格納されていた元の値を返します。
// すべての操作はアトミックに実行されます fn atomicExchange ( atomic_ptr : ptr< AS , atomic< T > , read_write> , v : T ) -> T { let old = * atomic_ptr ; * atomic_ptr = v ; return old ; }
17.8.5.
atomicCompareExchangeWeak
fn atomicCompareExchangeWeak ( atomic_ptr : ptr< AS , atomic< T > , read_write> , cmp : T , v : T ) -> __atomic_compare_exchange_result < T > struct __atomic_compare_exchange_result < T > { old_value : T , // アトミックに格納されていた古い値 exchanged : bool// 交換が実行された場合はtrue }
注:
__atomic_compare_exchange_result
型で値を明示的に宣言することはできませんが、型推論は可能です。
次のステップをアトミックに実行します:
-
atomic_ptr
が指す元の値をロードします。 -
元の値と
cmp
の値を等価演算で比較します。 -
等価比較の結果が
true
の場合のみ、値v
を格納します。
2つのメンバーからなる構造体を返します。最初のメンバーold_value
は操作前のアトミックオブジェクトの元の値で、2番目のメンバーexchanged
は比較が成功したかどうかを示します。
// すべての操作はアトミックに実行されます fn atomicCompareExchangeWeak ( atomic_ptr : ptr< AS , atomic< T > , read_write> , cmp : T , v : T ) -> _atomic_compare_exchange_result < T > { let old = * atomic_ptr ; // この比較は偽陽性で失敗する可能性があります。 let comparison = old == cmp ; if comparison { * atomic_ptr = v ; } return _atomic_compare_exchange_result < T > ( old , comparison ); }
注:
一部の実装では等価比較が偽陽性で失敗する場合があります。つまり、結果ベクトルの第2成分がfalse
となる場合でも、第1成分がcmp
と等しいことがあります。
17.9. データパッキング組み込み関数
データパッキング組み込み関数は、WGSLの型と直接対応しないデータフォーマットを使用して値をエンコードするために利用できます。 これにより、多くの密集した値をメモリに書き込むことが可能となり、シェーダのメモリ帯域幅要求を削減できます。
各組み込み関数は、複数の入力値にチャネル転送関数の逆を適用し、その結果を一つの出力値にまとめます。
注: unorm値のパッキングでは、正規化された浮動小数点値は区間[0.0, 1.0]にあります。
注: snorm値のパッキングでは、正規化された浮動小数点値は区間[-1.0, 1.0]にあります。
17.9.1.
pack4x8snorm
オーバーロード |
|
説明 |
4つの正規化された浮動小数点値を8ビット符号付き整数に変換し、それらを1つのu32 値にまとめます。
入力の各成分 |
17.9.2.
pack4x8unorm
オーバーロード |
|
説明 |
4つの正規化された浮動小数点値を8ビット符号なし整数に変換し、それらを1つのu32 値にまとめます。
入力の各成分 |
17.9.3. pack4xI8
オーバーロード |
|
説明 |
e の各コンポーネントの下位8ビットをu32値にパックし、未使用のビットはすべて捨てます。
入力のコンポーネント |
17.9.4. pack4xU8
オーバーロード |
|
説明 |
e の各コンポーネントの下位8ビットをu32値にパックし、未使用のビットはすべて捨てます。
入力のコンポーネント |
17.9.5.
pack4xI8Clamp
オーバーロード |
|
説明 |
e の各コンポーネントを範囲 [-128, 127] にクランプし、その後各コンポーネントの下位8ビットを u32 値にパックします。
入力のコンポーネント |
17.9.6.
pack4xU8Clamp
オーバーロード |
|
説明 |
e の各コンポーネントを [0, 255] の範囲でクランプし、その後各コンポーネントの下位8ビットを u32 値にパックします。
入力のコンポーネント |
17.9.7.
pack2x16snorm
オーバーロード |
|
説明 | 2つの正規化された浮動小数点値を16ビット符号付き整数に変換し、それらを1つのu32 値にまとめます。入力の各成分 e[i] は、16ビット2の補数整数値
⌊ 0.5 + 32767 × min(1, max(-1, e[i])) ⌋に変換され、
結果の16 × i から16 × i + 15までのビットに配置されます。
|
17.9.8.
pack2x16unorm
オーバーロード |
|
説明 | 2つの正規化された浮動小数点値を16ビット符号なし整数に変換し、それらを1つのu32 値にまとめます。入力の各成分 e[i] は、16ビット符号なし整数値
⌊ 0.5 + 65535 × min(1, max(0, e[i])) ⌋に変換され、
結果の16 × i から16 × i + 15までのビットに配置されます。
|
17.9.9.
pack2x16float
オーバーロード |
|
説明 |
2つの浮動小数点値を半精度浮動小数点数に変換し、それらを1つのu32 値にまとめます。入力の各成分 e[i] はIEEE-754 binary16
値に変換され、
結果の16 × i から16 × i + 15までのビットに配置されます。
§ 15.7.6 浮動小数点変換も参照してください。
もし
|
17.10. データアンパック組み込み関数
データアンパック組み込み関数は、WGSLの型と直接対応しないデータフォーマット内の値をデコードするために利用できます。 これにより、プログラムはメモリから多くの密集した値を読み取ることができ、シェーダのメモリ帯域幅の要求を削減できます。
各組み込み関数は入力値をチャネルに分割し、それぞれにチャネル転送関数を適用します。
注: unorm値のアンパックでは、正規化された浮動小数点の結果は区間[0.0, 1.0]にあります。
注: snorm値のアンパックでは、正規化された浮動小数点の結果は区間[-1.0, 1.0]にあります。
17.10.1.
unpack4x8snorm
オーバーロード |
|
説明 | 32ビット値を4つの8ビットチャンクに分解し、各チャンクを符号付き正規化浮動小数点値として再解釈します。 結果の成分 i はmax(v ÷ 127, -1)です。ここでv は
e の8×i から8×i + 7 のビットを2の補数符号付き整数として解釈した値です。
|
17.10.2.
unpack4x8unorm
オーバーロード |
|
説明 | 32ビット値を4つの8ビットチャンクに分解し、各チャンクを符号なし正規化浮動小数点値として再解釈します。 結果の成分 i はv ÷ 255です。ここでv は
e の8×i から8×i + 7 のビットを符号なし整数として解釈した値です。
|
17.10.3.
unpack4xI8
オーバーロード |
|
説明 | e は4つの8ビット符号付き整数成分を持つベクトルとして解釈されます。e を符号拡張付きのvec4<i32>にアンパックします。
|
17.10.4.
unpack4xU8
オーバーロード |
|
説明 | e は4つの8ビット符号なし整数成分を持つベクトルとして解釈されます。e をゼロ拡張付きのvec4<u32>にアンパックします。
|
17.10.5.
unpack2x16snorm
オーバーロード |
|
説明 | 32ビット値を2つの16ビットチャンクに分解し、各チャンクを符号付き正規化浮動小数点値として再解釈します。 結果の成分 i はmax(v ÷ 32767, -1)です。ここでv は
e の16×i から16×i + 15 のビットを2の補数符号付き整数として解釈した値です。
|
17.10.6.
unpack2x16unorm
オーバーロード |
|
説明 | 32ビット値を2つの16ビットチャンクに分解し、各チャンクを符号なし正規化浮動小数点値として再解釈します。 結果の成分 i はv ÷ 65535です。ここでv は
e の16×i から16×i + 15 のビットを符号なし整数として解釈した値です。
|
17.10.7.
unpack2x16float
オーバーロード |
|
説明 | 32ビット値を2つの16ビットチャンクに分解し、各チャンクを浮動小数点値として再解釈します。 結果の成分 i はv のf32表現です。ここでv は
e の16×i から16×i + 15 のビットをIEEE-754 binary16値として解釈したものです。
§ 15.7.6 浮動小数点変換も参照してください。
|
17.11. 同期組み込み関数
すべての同期関数は制御バリアを Acquire/Release メモリ順序で実行します。 つまり、すべての同期関数と、影響を受けるメモリおよびアトミック操作は、同期関数に対してプログラム順序で並べられます。 さらに、同期関数よりプログラム順序で前にある影響を受けるメモリやアトミック操作は、同期関数より後にワークグループのメンバーによって実行される任意の影響を受けるメモリやアトミック操作の前に、ワークグループ内のすべての他のスレッドに対して可視でなければなりません。
すべての同期関数はWorkgroup
のメモリスコープを使用します。
すべての同期関数はWorkgroup
の実行スコープを持ちます。
すべての同期関数は必須で
コンピュートシェーダステージのみで使用しなければなりません。
すべての同期関数は必須で
一様制御フロー内でのみ呼び出さなければなりません。
17.11.1.
storageBarrier
オーバーロード |
|
説明 | 制御バリア同期関数を実行し、 storageアドレス空間の メモリおよびアトミック操作に影響します。 |
17.11.2.
textureBarrier
オーバーロード |
|
説明 | 制御バリア同期関数を実行し、 handleアドレス空間の メモリ操作に影響します。 |
17.11.3.
workgroupBarrier
オーバーロード |
|
説明 | 制御バリア同期関数を実行し、 workgroupアドレス空間の メモリおよびアトミック操作に影響します。 |
17.11.4.
workgroupUniformLoad
オーバーロード |
|
パラメータ化 | T は具体的な構築可能型です。
|
説明 |
p が指す値をワークグループ内のすべての呼び出しに返します。
戻り値は一様です。
p は必須で一様値でなければなりません。
|
オーバーロード |
|
説明 |
p が指す値をアトミックにロードし、ワークグループ内のすべての呼び出しに返します。
戻り値は一様です。
p は必須で一様値でなければなりません。
|
17.12. サブグループ組み込み関数
これらの関数の呼び出しは:
-
トリガーされる subgroup_uniformity 診断は 一様性解析が呼び出しが 一様制御フロー内であることを証明できない場合に発生します。
注: コンピュートシェーダステージの場合、 一様制御フローのスコープはワークグループです。 フラグメントシェーダステージの場合、 一様制御フローのスコープは描画コマンドです。 これらのスコープはどちらもサブグループよりも大きいです。
17.12.1.
subgroupAdd
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
集約(リダクション)操作。 |
17.12.1.1. subgroupExclusiveAdd
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
除外プレフィックススキャン操作(エクスクルーシブスキャン)。
アクティブな呼び出しの中で最もIDが小さい呼び出しに対して返る値は |
17.12.1.2. subgroupInclusiveAdd
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
包含プレフィックススキャン操作(インクルーシブスキャン)。
注:
|
17.12.2.
subgroupAll
オーバーロード |
|
説明 | アクティブな呼び出しのすべてでe がtrue の場合にtrue を返します。対象はサブグループです。
|
17.12.3.
subgroupAnd
オーバーロード |
|
前提条件 | T はi32, u32, vecN<i32>, またはvecN<u32>です
|
説明 |
集約(リダクション)操作。 |
17.12.4.
subgroupAny
オーバーロード |
|
説明 | アクティブな呼び出しのいずれかでe がtrue の場合にtrue を返します。対象はサブグループです。
|
17.12.5.
subgroupBallot
オーバーロード |
|
説明 |
アクティブな呼び出しのうち、pred がtrue であるもののビットマスクを
サブグループ内で返します。戻り値のx成分は呼び出しID 0〜31を含みます。 各成分内では、IDはビット位置で昇順です(例:ID 32はy成分のビット位置0)。 |
17.12.6.
subgroupBroadcast
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルI はu32 または i32です
|
説明 |
サブグループ呼び出しIDがid と一致する呼び出しのe の値を、サブグループ内のすべてのアクティブな呼び出しに返します。
注: 非定数の |
17.12.6.1. subgroupBroadcastFirst
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 | アクティブな呼び出しの中で最も小さいサブグループ呼び出しIDを持つ呼び出しのe の値を、サブグループ内のすべてのアクティブな呼び出しに返します。
|
17.12.7.
subgroupElect
オーバーロード |
|
説明 | 現在の呼び出しがアクティブな呼び出しの中で最も小さいサブグループ呼び出しIDを持つ場合true を返します。対象はサブグループです。
|
17.12.8.
subgroupMax
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
集約(リダクション)操作。 |
17.12.9.
subgroupMin
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
集約(リダクション)操作。 |
17.12.10.
subgroupMul
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
集約(リダクション)操作。 |
17.12.10.1. subgroupExclusiveMul
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
除外プレフィックススキャン操作(エクスクルーシブスキャン)。 アクティブな呼び出しのうち、現在の呼び出しIDより小さい
サブグループ呼び出しIDを持つ呼び出し全体で アクティブな呼び出しのうち最もIDが小さい呼び出しに対して返る値は |
17.12.10.2. subgroupInclusiveMul
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
包含プレフィックススキャン操作(インクルーシブスキャン)。 アクティブな呼び出しのうち、現在の呼び出しID以下の
サブグループ呼び出しIDを持つ呼び出し全体で 注:
|
17.12.11.
subgroupOr
オーバーロード |
|
前提条件 | T はi32, u32, vecN<i32>, またはvecN<u32>です
|
説明 |
集約(リダクション)操作。 |
17.12.12.
subgroupShuffle
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルI はu32 または i32です
|
説明 |
サブグループ呼び出しIDがid と一致する呼び出しのe を返します。
もし
|
17.12.12.1. subgroupShuffleDown
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
現在の呼び出しについて、サブグループ呼び出しIDがsubgroup_invocation_id + delta と一致する呼び出しのe を返します。
もし
|
17.12.12.2. subgroupShuffleUp
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
現在の呼び出しについて、サブグループ呼び出しIDがsubgroup_invocation_id - delta と一致する呼び出しのe を返します。
もし
|
17.12.12.3. subgroupShuffleXor
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
現在の呼び出しについて、サブグループ呼び出しIDがsubgroup_invocation_id ^ mask と一致する呼び出しのe を返します。
もし
|
17.12.13.
subgroupXor
オーバーロード |
|
前提条件 | T はi32, u32, vecN<i32>, またはvecN<u32>です
|
説明 |
集約(リダクション)操作。 |
17.13. クアッド操作
§ 15.6.4 クアッド操作を参照。
これらの関数の呼び出しは:
-
トリガーされる subgroup_uniformity 診断は 一様性解析が呼び出しが 一様制御フロー内であることを証明できない場合に発生します。
注: コンピュートシェーダステージの場合、 一様制御フローのスコープはワークグループです。 フラグメントシェーダステージの場合、 一様制御フローのスコープは描画コマンドです。 これらのスコープはどちらもクアッドよりも大きいです。
17.13.1.
quadBroadcast
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルI はu32またはi32です
|
説明 |
クアッド呼び出しIDがid と一致する呼び出しのe の値を、クアッド内のすべてのアクティブな呼び出しに返します。
注: subgroupBroadcastとは異なり、現時点では非定数の代替手段はありません。 |
17.13.2.
quadSwapDiagonal
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
クアッド内で対角にある呼び出しのe の値を返します。
すなわち:
|
17.13.3.
quadSwapX
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
クアッド内で同じX次元を共有する呼び出しのe の値を返します。
すなわち:
|
17.13.4.
quadSwapY
オーバーロード |
|
前提条件 | T は具体的な数値スカラーまたは数値ベクトルです
|
説明 |
クアッド内で同じY次元を共有する呼び出しのe の値を返します。
すなわち:
|
18. 再帰下降構文解析用の文法
この章は規範的ではありません。
WGSLの文法はLALR(1)パーサに適した形式で定義されています。 実装によっては、再帰下降パーサを使いたい場合もあります。
規範的な文法は、再帰下降パーサには直接利用できません。なぜなら、いくつかの規則が左再帰的だからです。 文法規則が直接左再帰であるとは、定義されている非終端記号がその生成規則の最初に現れる場合を指します。
以下はWGSL文法ですが、機械的に次のように変換されています:
-
直接・間接左再帰の除去。
-
空生成規則(イプシロン規則)の回避。
-
兄弟規則間の共通プレフィックスの統合。
ただし、LL(1)ではありません。
非終端記号によっては、複数の生成規則が共通の先読み集合を持ちます。
例として、attribute
非終端記号のすべての生成規則は attr
トークンで始まります。
より微妙な例としては global_decl
で、3つの生成規則が attribute *
句で始まり、
その後 fn
, override
, var
トークンで区別されます。
簡潔さのため、多くのトークン定義は繰り返されません。 トークン定義は仕様書の主部のものを利用してください。
| '+'
| '-'
| '('
( expression ( ','
expression )* ','
? )?
')'
| compound_assignment_operator
| '='
| '@'
ident_pattern_token ( '('
( expression ( ','
expression )* ','
? )?
')'
)?
| '@'
'align'
'('
expression ','
? ')'
| '@'
'binding'
'('
expression ','
? ')'
| '@'
'blend_src'
'('
expression ','
? ')'
| '@'
'builtin'
'('
builtin_value_name ','
?
')'
| '@'
'diagnostic'
diagnostic_control
| '@'
'group'
'('
expression ','
? ')'
| '@'
'id'
'('
expression ','
? ')'
| '@'
'location'
'('
expression ','
? ')'
| '@'
'size'
'('
expression ','
?
')'
| '&'
unary_expression ( '&'
unary_expression )*
| '^'
unary_expression ( '^'
unary_expression )*
| '|'
unary_expression ( '|'
unary_expression )*
| 'false'
| 'true'
| 'default'
| '.'
member_ident component_or_swizzle_specifier
?
| '.'
swizzle_name component_or_swizzle_specifier
?
| '['
expression ']'
component_or_swizzle_specifier
?
| '%='
| '&='
| '*='
| '+='
| '-='
| '/='
| '^='
| '|='
| '@'
'compute'
| '@'
'const'
| ident
| '('
lhs_expression ')'
| /0[fh]/
| /[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?[fh]?/
| /[0-9]+[eE][+-]?[0-9]+[fh]?/
| /[0-9]+\.[0-9]*([eE][+-]?[0-9]+)?[fh]?/
| /[1-9][0-9]*[fh]/
| /0[iu]?/
| /[1-9][0-9]*[iu]?/
| '('
ident_pattern_token ','
diagnostic_rule_name ','
? ')'
| unary_expression bitwise_expression.post.unary_expression
| unary_expression relational_expression.post.unary_expression
| unary_expression relational_expression.post.unary_expression
'&&'
unary_expression relational_expression.post.unary_expression
( '&&'
unary_expression relational_expression.post.unary_expression
)*
| unary_expression relational_expression.post.unary_expression
'||'
unary_expression relational_expression.post.unary_expression
( '||'
unary_expression relational_expression.post.unary_expression
)*
| '@'
'fragment'
| template_elaborated_ident.post.ident
'('
( expression ( ','
expression )* ','
? )?
')'
| 'const_assert'
';'
| attribute * 'fn'
ident '('
( attribute * ident
':'
type_specifier ( ','
param )* ','
? )? ')'
(
'->'
attribute * ident template_elaborated_ident.post.ident
)? attribute * '{'
statement * '}'
| attribute * 'var'
( _template_args_start expression ( ','
expression )* ','
? _template_args_end )? optionally_typed_ident (
'='
expression )? ';'
| global_value_decl ';'
| 'alias'
ident '='
ident template_elaborated_ident.post.ident
';'
| 'struct'
ident '{'
attribute * member_ident ':'
type_specifier ( ','
attribute * member_ident ':'
type_specifier )* ','
?
'}'
| 'diagnostic'
'('
ident_pattern_token ','
diagnostic_rule_name
','
? ')'
';'
| 'enable'
ident_pattern_token ( ','
ident_pattern_token )* ','
?
';'
| 'requires'
ident_pattern_token ( ','
ident_pattern_token )* ','
?
';'
| attribute * 'override'
optionally_typed_ident (
'='
expression )?
| 'const'
optionally_typed_ident
'='
expression
| /0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+([pP][+-]?[0-9]+[fh]?)?/
| /0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+[fh]?/
| /0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*([pP][+-]?[0-9]+[fh]?)?/
| '@'
'interpolate'
'('
ident_pattern_token
','
? ')'
| '@'
'interpolate'
'('
ident_pattern_token
','
ident_pattern_token ','
?
')'
| '@'
'invariant'
| core_lhs_expression component_or_swizzle_specifier ?
| '&'
lhs_expression
| '*'
lhs_expression
| '%'
| '*'
| '/'
| '@'
'must_use'
| ident ( ':'
type_specifier )?
| attribute * ident
':'
type_specifier
| ident template_elaborated_ident.post.ident
| ident template_elaborated_ident.post.ident argument_expression_list
| literal
| '('
expression ')'
| shift_expression.post.unary_expression
| shift_expression.post.unary_expression greater_than unary_expression shift_expression.post.unary_expression
| shift_expression.post.unary_expression greater_than_equal unary_expression shift_expression.post.unary_expression
| shift_expression.post.unary_expression less_than unary_expression shift_expression.post.unary_expression
| shift_expression.post.unary_expression less_than_equal unary_expression shift_expression.post.unary_expression
| shift_expression.post.unary_expression
'!='
unary_expression shift_expression.post.unary_expression
| shift_expression.post.unary_expression
'=='
unary_expression shift_expression.post.unary_expression
| ( multiplicative_operator unary_expression )* ( additive_operator unary_expression ( multiplicative_operator unary_expression )* )*
| attribute * 'for'
'('
for_init ? ';'
expression ? ';'
for_update ? ')'
compound_statement
| attribute * 'if'
expression compound_statement (
'else'
'if'
expression compound_statement )* (
'else'
compound_statement )?
| attribute * 'loop'
attribute * '{'
statement * ( 'continuing'
attribute * '{'
statement * ( 'break'
'if'
expression ';'
)?
'}'
)? '}'
| attribute * 'switch'
expression attribute * '{'
switch_clause * '}'
| attribute * 'while'
expression compound_statement
| ident template_elaborated_ident.post.ident
argument_expression_list
';'
| variable_or_value_statement
';'
| variable_updating_statement
';'
| assert_statement ';'
| break_statement ';'
| continue_statement ';'
| ';'
| 'discard'
';'
| 'return'
expression ? ';'
| 'case'
case_selector ( ','
case_selector )* ','
?
':'
? compound_statement
| 'default'
':'
? compound_statement
| /[rgba]/
| /[rgba][rgba]/
| /[rgba][rgba][rgba]/
| /[rgba][rgba][rgba][rgba]/
| /[xyzw]/
| /[xyzw][xyzw]/
| /[xyzw][xyzw][xyzw]/
| /[xyzw][xyzw][xyzw][xyzw]/
| ( _template_args_start template_arg_expression (
','
expression )* ','
? _template_args_end )?
| global_directive * ( global_decl | global_assert | ';'
) *
| ident ( _template_args_start template_arg_expression (
','
expression )* ','
? _template_args_end )?
| primary_expression component_or_swizzle_specifier ?
| '!'
unary_expression
| '&'
unary_expression
| '*'
unary_expression
| '-'
unary_expression
| '~'
unary_expression
| 'var'
( _template_args_start expression ( ','
expression )* ','
? _template_args_end )? optionally_typed_ident
| variable_decl '='
expression
| 'const'
optionally_typed_ident
'='
expression
| 'let'
optionally_typed_ident
'='
expression
| lhs_expression ( '='
| compound_assignment_operator
) expression
| lhs_expression '++'
| lhs_expression '--'
| '_'
'='
expression
| '@'
'vertex'
| '@'
'workgroup_size'
'('
expression ','
?
')'
| '@'
'workgroup_size'
'('
expression ','
expression ','
?
')'
| '@'
'workgroup_size'
'('
expression ','
expression ','
expression ','
?
')'
付録A: text/wgsl
メディアタイプ
Internet Assigned Numbers Authority (IANA) はメディアタイプのレジストリを管理しています。詳細は [IANA-MEDIA-TYPES] をご覧ください。
以下は WGSL モジュール向け text/wgsl
メディアタイプの定義です。
これは IANA に登録されており、
https://www.iana.org/assignments/media-types/text/wgsl
に掲載されています。
- タイプ名
-
text
- サブタイプ名
-
wgsl
- 必須パラメータ
-
N/A
- オプションパラメータ
-
なし
- エンコーディングに関する考慮事項
-
binary
WGSLはUTF-8エンコーディングを使用するUnicodeテキストであり、バイトオーダーマーク(BOM)はありません。 詳細は § 3 テキスト構造 を参照してください。
- セキュリティに関する考慮事項
-
WebGPU Shading Language (WGSL) はGPUコードのためのプログラミング言語であり、WebGPU API のコンテキストで実行されます。セキュリティに関する考慮事項については [WebGPU] の2.1節をご覧ください。 プライバシーに関する考慮事項については [WebGPU] の2.2節をご覧ください。
- 相互運用性に関する考慮事項
-
WebGPUの実装ごとに異なる機能を持つ場合があり、これらの違いはWGSLプログラムで利用できる機能に影響を及ぼします。詳細は [WebGPU] の3.6節や、 § 4.1.2 言語拡張 を参照してください。
本登録はWGSLの後続版にも適用されることが期待されており、公開仕様の参照も随時更新される可能性があります。このような期待はメディアタイプ登録の中では異例ですが、業界慣習には合致します。
- 公開仕様
-
WebGPU Shading Language
- このメディアタイプを利用するアプリケーション
-
WebGPUの実装。これにはWebブラウザも含まれると想定されます。
- フラグメント識別子の考慮事項
-
なし
- 追加情報
-
マジックナンバー: なし
ファイル拡張子:
.wgsl
Macintoshファイルタイプコード:
TEXT
- 追加情報の問い合わせ先(氏名・メールアドレス)
-
David Neto, dneto@google.com、または WGSL に記載されている編集者。
- 意図された用途
-
COMMON
- 著者
-
W3C。WGSLに記載の編集者を参照。
- 変更管理者
-
W3C
- 規範的参考文献
-
[WebGPU] W3C, "WebGPU” W3C Working Draft, January 2023. https://w3.org/TR/webgpu
Webgpu Shading Language W3C, "WebGPU Shading Language" W3C Working Draft, January 2023. https://w3.org/TR/WGSL