DirectX(D3D11.1)再入門 ShaderReflection

More than 3 years have passed since last update.

さっそく予定を変更して今回はShaderReflectionについて。コンパイルされたシェーダーからいろいろな情報を引き出せますよと。

ConstantBufferをやろうと思ったのだけど、その時もShaderReflectionを活用した方がいろいろ都合がよろしいので先にShaderReflectionをやることにした。

今回のコード

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


入力レイアウト

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から動的に構築する。


インクルードとリンク

#include <d3dcompiler.h>

として

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

d3dcompiler.lib

dxguid.lib

dxguidが無いと

リンクエラー _IID_ID3D11ShaderReflection

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


追加コード

前回のコードとの差分。


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を作った。


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から得るのがポイントで次のような関数を作った(網羅はしてない)。


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配列の生成は完了。

これでシェーダーに書いてあることを2度書く必要は無くなる。