変数構文
HLSL 変数を宣言するには、次の構文規則を使用します。
[Storage_Class] [Type_Modifier] タイプ名[インデックス] [: セマンティック] [: Packoffset] [: レジスタ]; [注釈] [= Initial_Value]
パラメーター
-
Storage_Class
-
変数のスコープと有効期間についてコンパイラにヒントを与えるオプションのストレージ クラス修飾子。修飾子は任意の順序で指定できます。
値 説明 extern グローバル変数をシェーダーへの外部入力としてマークします。これはすべてのグローバル変数のデフォルトのマークです。 staticと組み合わせることはできません。 nointerpolation 頂点シェーダの出力をピクセル シェーダに渡す前に補間しないでください。 precise precise キーワードを変数に適用すると、その変数に割り当てられる値を生成するために使用される計算が次のように制限されます。 - 個別の操作は個別に保持されます。 たとえば、mul 演算と add 演算が mad 演算に融合されている可能性がある場合、 precise は演算を強制的に分離したままにします。 代わりに、 mad 組み込み関数を明示的に使用する必要があります。
- 操作の順序は維持されます。 パフォーマンスを向上させるために命令の順序が変更される可能性がある場合、 precise により、コンパイラは記述どおりの順序を保持するようになります。
- IEEE 安全でない操作は制限されます。 コンパイラが NaN (非数) 値と INF (無限) 値を考慮しない高速数学演算を使用した場合、 precise は NaN 値と INF 値に関する IEEE 要件を遵守するように強制します。 preciseがなければ、これらの最適化と数学演算は IEEE に準拠しません。
- 変数を precise と修飾しても、その変数を使用する操作は preciseにはなりません。 precise は、 precise 修飾変数に割り当てられる値に寄与する演算にのみ伝播するため、必要な計算 precise を正しく行うことは難しい場合があります。そのため、構造体フィールド、出力パラメーター、またはエントリ関数の戻り値の型など、宣言する場所でシェーダー出力 precise を直接マークすることをお勧めします。 このように最適化を制御する機能により、累積された精度の差によって最終結果に影響を与える可能性のある最適化を無効にすることで、変更された出力変数の結果の不変性が維持されます。 これは、テッセレーション用のシェーダーでパッチの継ぎ目を密閉したままにしたり、複数のパスにわたって深度値を一致させたりする場合に便利です。 サンプル コード:
HLSLmatrix g_mWorldViewProjection;
void main(in float3 InPos : Position, out precise float4 OutPos : SV_Position)
=
// 操作は正確なパラメータ OutPos に寄与するため正確です
OutPos = mul( float4( InPos, 1.0 ), g_mWorldViewProjection );
}
shared エフェクト間で共有する変数をマークします。これはコンパイラへのヒントです。 groupshared コンピュート シェーダーのスレッド グループ共有メモリの変数をマークします。 D3D10 では、グループ共有ストレージ クラスを持つすべての変数の最大合計サイズは 16 KB ですが、D3D11 では最大サイズは 32 KB です。 例を参照してください。 static ローカル変数をマークして、一度初期化され、関数呼び出し間で保持されるようにします。 宣言に初期化子が含まれていない場合、値はゼロに設定されます。 static とマークされたグローバル変数はアプリケーションからは見えません。 uniform シェーダの実行全体を通じてデータが一定である変数 (頂点シェーダのマテリアル カラーなど) をマークします。グローバル変数は、デフォルトでは uniform であると見なされます。 volatile 頻繁に変更される変数をマークします。これはコンパイラへのヒントになります。 このストレージ クラス修飾子はローカル変数にのみ適用されます。
注: HLSL コンパイラは現在、このストレージ クラス修飾子を無視します。 -
Type_Modifier
-
オプションの変数型修飾子。
値 説明 const シェーダーによって変更できない変数をマークします。そのため、変数宣言で初期化する必要があります。 グローバル変数は、デフォルトでは const と見なされます (コンパイラに /Gec フラグを指定すると、この動作が抑制されます)。 row_major 4 つのコンポーネントを 1 つの行に格納する変数をマークして、それらを 1 つの定数レジスタに格納できるようにします。 column_major 行列計算を最適化するために、1 つの列に 4 つのコンポーネントを格納する変数をマークします。 Note
型修飾子の値を指定しない場合、コンパイラはデフォルト値として column_major を使用します。
-
Type
-
データ型 (DirectX HLSL)にリストされている任意の HLSL 型。
-
名前[インデックス]
-
シェーダ変数を一意に識別する ASCII 文字列。 オプションの配列を定義するには、配列のサイズに index を使用します。これは正の整数 = 1 です。
-
セマンティック
-
シェーダの入力と出力をリンクするためにコンパイラによって使用されるオプションのパラメータ使用情報。 頂点シェーダーとピクセル シェーダーには、いくつかの定義済みの セマンティクス があります。 コンパイラは、グローバル変数またはシェーダーに渡されるパラメーターで宣言されていない限り、セマンティクスを無視します。
-
Packoffset
-
シェーダ定数を手動でパックするためのオプションのキーワード。 packoffset (DirectX HLSL)を参照してください。
-
[登録]
-
シェーダ変数を特定のレジスタに手動で割り当てるためのオプションのキーワード。 レジスタ(DirectX HLSL)を参照してください。
-
注釈
-
グローバル変数に添付される文字列形式のオプションのメタデータ。 注釈はエフェクト フレームワークによって使用され、HLSL では無視されます。詳細な構文については、 注釈構文を参照してください。
-
Initial_Value
-
オプションの初期値。値の数は、 Typeのコンポーネントの数と一致する必要があります。 extern とマークされた各グローバル変数はリテラル値で初期化する必要があります。 static とマークされた各変数は定数で初期化する必要があります。
static または extern としてマークされていないグローバル変数は、シェーダーにコンパイルされません。 コンパイラはグローバル変数のデフォルト値を自動的に設定しないため、最適化でそれらを使用できません。 このタイプのグローバル変数を初期化するには、リフレクションを使用してその値を取得し、その値を定数バッファーにコピーします。 たとえば、 ID3D11ShaderReflection::GetVariableByName メソッドを使用して変数を取得し、 ID3D11ShaderReflectionVariable::GetDesc メソッドを使用してシェーダー変数の説明を取得し、 D3D11_SHADER_VARIABLE_DESC 構造体の DefaultValue メンバーから初期値を取得できます。 値を定数バッファーにコピーするには、バッファーが CPU 書き込みアクセス (D3D11_CPU_ACCESS_WRITE) を使用して作成されたことを確認する必要があります。 定数バッファを作成する方法の詳細については、「定数バッファを作成する方法」を参照してください。
エフェクトフレームワーク を使用して、反射処理や初期値の設定を自動で行うこともできます。 たとえば、 ID3DX11EffectPass::Apply メソッドを使用できます。
重要
この機能のサポートは、デフォルトの初期化子を反映する機能を含め、Direct3D 12 で削除されました。
例
シェーダー変数宣言の例をいくつか示します。
float fVar;
float4 color;
int iVar[3];
uniform float4 position : SV_POSITION;
//Default initializers; supported up to Direct3D 11.
float fVar = 3.1f;
int iVar[3] = {1,2,3};
const float4 lightDirection = {0,0,1};
グループ共有
HLSL を使用すると、コンピューティング シェーダーのスレッドが共有メモリを介して値を交換できるようになります。 HLSL は、シェーダー内の共有メモリへの読み取りと書き込みの正しい順序を保証し、データ競合を回避するために、 GroupMemoryBarrierWithGroupSyncなどのバリア プリミティブを提供します。
Note
ハードウェアはスレッドをグループ (ワープまたはウェーブフロント) で実行し、同じグループに属するスレッドのみを同期することが正しい場合は、バリア同期を省略してパフォーマンスを向上できる場合があります。 しかし、次の理由から、この省略は強くお勧めしません。
- この省略により、移植性のないコードが生成され、一部のハードウェアでは動作しない可能性があり、通常は小さなグループでスレッドを実行するソフトウェア ラスタライザーでは動作しません。
- これを省略することで達成できるパフォーマンスの向上は、全スレッド バリアを使用する場合に比べるとわずかです。
Direct3D 10 では、 groupshared に書き込むときにスレッドの同期が行われないため、各スレッドは書き込み用に配列内の 1 つの場所に制限されます。 2 つのスレッドが衝突しないように、書き込み時に SV_GroupIndex システム値を使用してこの配列にインデックスを付けます。 読み取りに関しては、すべてのスレッドが読み取りのために配列全体にアクセスできます。
struct GSData
{
float4 Color;
float Factor;
}
groupshared GSData data[5*5*1];
[numthreads(5,5,1)]
void main( uint index : SV_GroupIndex )
{
data[index].Color = (float4)0;
data[index].Factor = 2.0f;
GroupMemoryBarrierWithGroupSync();
...
}
梱包
レジスタ境界を越えないように十分な大きさのベクトルとスカラーのサブコンポーネントをパックします。 たとえば、以下はすべて有効です。
cbuffer MyBuffer
{
float4 Element1 : packoffset(c0);
float1 Element2 : packoffset(c1);
float1 Element3 : packoffset(c1.y);
}
梱包タイプを混在させることはできません。
register キーワードと同様に、packoffset はターゲット固有にすることができます。 サブコンポーネントのパッキングは、register キーワードではなく、packoffset キーワードでのみ使用できます。 cbuffer 宣言内では、クロスプラットフォームの互換性のためであると想定されるため、Direct3D 10 ターゲットでは register キーワードは無視されます。
パックされた要素は重複する可能性があり、コンパイラはエラーや警告を出力しません。 この例では、Element2 と Element3 は Element1.x および Element1.y と重なります。
cbuffer MyBuffer
{
float4 Element1 : packoffset(c0);
float1 Element2 : packoffset(c0);
float1 Element3 : packoffset(c0.y);
}
packoffset を使用するサンプルは次のとおりです: HLSLWithoutFX10 Sample。