LoginSignup
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度書く必要は無くなる。

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
What you can do with signing up
9