DirectX(D3D11.1)再入門 ShaderReflection

さっそく予定を変更して今回はShaderReflectionについて。コンパイルされたシェーダーからいろいろな情報を引き出せますよと。
ConstantBufferをやろうと思ったのだけど、その時もShaderReflectionを活用した方がいろいろ都合がよろしいので先にShaderReflectionをやることにした。

[今回のコード](https://github.com/ousttrue/MinTriangle/tree/Reflection)

ShaderReflectionを使ってVertexShaderから入力レイアウトを生成してみる。

```cpp:入力レイアウト
D3D11_INPUT_ELEMENT_DESC vbElement[] =
        {
            { 
            "POSITION" // SemanticName
            , 0
            , DXGI_FORMAT_R32G32B32A32_FLOAT
            , 0
            , 0
            , D3D11_INPUT_PER_VERTEX_DATA
            , 0 
            },
            { 
            "COLOR" // SemanticName
            , 0
            , DXGI_FORMAT_R32G32B32A32_FLOAT
            , 0
            , D3D11_APPEND_ALIGNED_ELEMENT
            , D3D11_INPUT_PER_VERTEX_DATA
            , 0 
            }
        };
```

上記のレイアウト情報をコンパイル済みVertexShaderから動的に構築する。

## インクルードとリンク

```cpp
#include <d3dcompiler.h>
```

として

以下のファイルへのリンクを追加する

```
d3dcompiler.lib
dxguid.lib
```

dxguidが無いと

```
リンクエラー _IID_ID3D11ShaderReflection
```

が出て悩まされることになる(悩んだ)。

## 追加コード

前回のコードとの差分。

```cpp:Reflection
		// vertex shader
		ResPtr<ID3DBlob> vblob;
		HRESULT hr = CompileShaderFromFile(shaderFile.c_str(), vsFunc.c_str(), "vs_4_0_level_9_1", &vblob);
		if (FAILED(hr))
			return false;
		hr = pDevice->CreateVertexShader(vblob->GetBufferPointer(), vblob->GetBufferSize(), NULL, &m_pVsh);
		if (FAILED(hr))
			return false;

		// vertex shader reflection
		ResPtr<ID3D11ShaderReflection> pReflector;
		hr = D3DReflect(vblob->GetBufferPointer(), vblob->GetBufferSize(), IID_ID3D11ShaderReflection, (void**)&pReflector);
		if (FAILED(hr))
			return false;
```

VertexShaderをコンパイルしてReflectionを作った。

```cpp:InputAssemblerレイアウトを取得する
		D3D11_SHADER_DESC shaderdesc;
		pReflector->GetDesc(&shaderdesc);

		// Create InputLayout
		std::vector<D3D11_INPUT_ELEMENT_DESC> vbElement;
		for (int i = 0; i < shaderdesc.InputParameters; ++i){
			D3D11_SIGNATURE_PARAMETER_DESC sigdesc;
			pReflector->GetInputParameterDesc(i, &sigdesc);

			auto format = GetDxgiFormat(sigdesc.ComponentType, sigdesc.Mask);

			D3D11_INPUT_ELEMENT_DESC eledesc = {
				sigdesc.SemanticName // Semantic名
				, sigdesc.SemanticIndex // POSITION0とかの数字。無ければ0
				, format // DXGI_FORMAT
				, 0 // 決め打ち
				, D3D11_APPEND_ALIGNED_ELEMENT // 決め打ち
				, D3D11_INPUT_PER_VERTEX_DATA // 決め打ち
				, 0 // 決め打ち
			};
			vbElement.push_back(eledesc);
		}

		if (!vbElement.empty()){
			hr = pDevice->CreateInputLayout(&vbElement[0], vbElement.size(),
				vblob->GetBufferPointer(), vblob->GetBufferSize(), &m_pInputLayout);
			if (FAILED(hr))
				return false;
		}
```

DXGI_FORMATをD3D11_SIGNATURE_PARAMETER_DESCから得るのがポイントで次のような関数を作った(網羅はしてない)。

```cpp:GetDxgiFormat
	DXGI_FORMAT GetDxgiFormat(D3D10_REGISTER_COMPONENT_TYPE type, BYTE mask)
	{
		if (mask & 0x0F)
		{
			// xyzw
			switch (type)
			{
			case D3D10_REGISTER_COMPONENT_FLOAT32:
				return DXGI_FORMAT_R32G32B32A32_FLOAT;
			}
		}

		if (mask & 0x07)
		{
			// xyz
			switch (type)
			{
			case D3D10_REGISTER_COMPONENT_FLOAT32:
				return DXGI_FORMAT_R32G32B32_FLOAT;
			}
		}

		if (mask & 0x3)
		{
			// xy
			switch (type)
			{
			case D3D10_REGISTER_COMPONENT_FLOAT32:
				return DXGI_FORMAT_R32G32_FLOAT;
			}
		}

		if (mask & 0x1)
		{
			// x
			switch (type)
			{
			case D3D10_REGISTER_COMPONENT_FLOAT32:
				return DXGI_FORMAT_R32_FLOAT;
			}
		}

		return DXGI_FORMAT_UNKNOWN;
	}
```

mask変数は各ビットがxyzwを使っているか否かに対応する。

以上で、D3D11_INPUT_ELEMENT_DESC配列の生成は完了。
これでシェーダーに書いてあることを２度書く必要は無くなる。