LoginSignup
11
9

More than 5 years have passed since last update.

DirectX(D3D11.1)再入門 ShaderReflection

Posted at

さっそく予定を変更して今回は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度書く必要は無くなる。

11
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9