日別アーカイブ: 2007年9月1日

Direct3D 10 Shader4.0 シェーダーのコンパイル

再び HLSL (Effect) のコンパイル関連です。
Direct3D 10 Shader4.0 APIによってコンパイラが違う
で少々調べたので、もう少し説明してみます。
マニュアルに詳しく書かれていないこと、わかりにくいこと、
実際に試したことなどなどをメモしていきます。

API まとめ

・Compile 系 API  HLSL/fx → Blob のバイトコード

ID3D10Effect/D3D10CompileEffectFromMemory()
ID3D10Shader/D3D10CompileShader()
D3DX10/D3DX10CompileFromFile()
D3DX10/D3DX10CompileFromMemory()
D3DX10/D3DX10CompileFromResource()
   
・Create 系 API  バイトコード → インターフェース

ID3D10Device::CreateVertexShader()
ID3D10Device::CreatePixelShader()
ID3D10Device::CreateGeometryShader()
ID3D10Device::CreateGeometryShaderWithStreamOutput()
ID3D10Effect/D3D10CreateEffectFromMemory()
ID3D10Effect/D3D10CreateEffectPoolFromMemory()
D3DX10/D3DX10CreateEffectFromFile()
D3DX10/D3DX10CreateEffectFromMemory()
D3DX10/D3DX10CreateEffectFromResource()
D3DX10/D3DX10CreateEffectPoolFromFile()
D3DX10/D3DX10CreateEffectPoolFromMemory()
D3DX10/D3DX10CreateEffectPoolFromResource()
D3DX10/D3DX10CreateAsync~

・Disassemble 系 API  インターフェース → Blob の disassemble リスト

ID3D10Shader/D3D10DisassembleShader()
ID3D10Effect/D3D10DisassembleEffect()
D3DX10/D3DX10DisassembleShader()
D3DX10/D3DX10DisassembleEffect()

・Preprocessor 系 API  HLSL/fx → Blob のテキスト

ID3D10Shader/D3D10PreprocessShader()
D3DX10/D3DX10PreprocessShaderFromFile()
D3DX10/D3DX10PreprocessShaderFromMemory()
D3DX10/D3DX10PreprocessShaderFromResource()

必要な機能がほとんどそろっています。fxc.exe はコマンド
ライン引数のパースだけで、あとは D3DX10 を呼び出している
だけのようです。

D3DX10 の D3DX10Create~ は HLSL からのコンパイルだけでなく、
コンパイル済みデータ(fxo)を受け取ることも出来ます。

Preprocessor は Include や Macro 等のテキスト処理を行い、
結果を返してくれる便利な関数です。まず、シェーダー
コンパイル時にマクロの適用がきちんと行われているか確認
することができます。

またシェーダーにこだわらず、script 系言語の汎用のテキスト
処理フィルタとして流用できるかもしれません。

ただ一旦パーサにかけたテキストを再構築して出力している
らしく、無駄な空白や空行が除去され各キーワードがスペース
区切りに置き換わっています。
わかりやすいし処理しやすいものの、#line 等のディレクティブも
失われてしまいます。
本当のプリプロセッサとして活用する場合は、エラー時の行番号
を求めることが出来ず、デバッグしづらくなるのが難点でしょう。

Disassemble/Preprocessor など Blob のテキストで返す API は
‘\0’ 終端でかつ、ID3D10Blob::GetBufferSize() が ‘\0’ を
含めたサイズを返す点に注意です。

例えばマニュアルの D3D10DisassembleEffect の説明には下記の
プログラム例が載っています。(下記はマニュアルからの引用)

LPCSTR commentString = NULL;
ID3D10Blob* pIDisassembly = NULL;
char* pDisassembly = NULL;
if( pVSBuf )
{
    D3D10DisassembleEffect( (UINT*)
    	l_pBlob_Effect->GetBufferPointer(),
        l_pBlob_Effect->GetBufferSize(), TRUE, commentString,
	&pIDisassembly );
    if( pIDisassembly )
    {
        FILE* pFile = fopen( "effect.htm", "w" );
        if( pFile)
        {
            fputs( (char*)pIDisassembly->GetBufferPointer(), pFile );
            fclose( pFile );
        }
    }
}

もしこれを次のように書くと、テキストファイルの終端に ‘\0’ が
含まれてしまいます。
(__Write は指定バイト数ファイルに書き込む仮想 API とする)

__Write( pIDisassembly->GetBufferPointer(),
		pIDisassembly->GetBufferSize() );

D3DX10CompileFromFile() は開始関数名の指定があるので、一見
Shader コンパイル専用の関数に見えます。実際は Effect(fx) の
コンパイルも可能です。この場合関数名 pFunctionName には NULL
を渡します。

Compile 系 API に渡す D3D10_SHADER_MACRO は、#define ~ に
相当する定義リストを渡すためのものです。この定義リストは
定義内容を文字列で渡す必要があり、かつ NULL 終端です。
マニュアルの D3D10_SHADER_MACRO の記述例では size [1] の配列を
使っていてこの NULL 終端が含まれていないので注意です。

以下マニュアルより引用

D3D10_SHADER_MACRO Shader_Macros[1] = { "zero", "0"  };

実際に使う場合はこんな感じです。

D3D10_SHADER_MACRO Shader_Macros[]= {
	{  "zero", "0"  },
	{  "TEXLAYER", "2"  },
	{  "POINTLIGHT", "8"  },
	{  NULL, NULL  },
};

HYPERでんち の方もいくつか更新しました。
シェーダーの世代ごとの違い
DirectX SDK バージョン一覧