BaseDx12(Dx12研究とフレームワーク)

【第1部】シンプルバージョン編

1202.複数のオブジェクトの描画(2)

 前項に引き続きSimpleSample102の解説です。

GameDevice::LoadAssets()関数

 前項ではGameDevice::LoadPipeline()関数を説明しました。
 この項では、GameDevice::LoadAssets()関数を説明します。この関数は、個別のオブジェクトの準備のような意味合いです。
 以下に関数全体を紹介します。
    void GameDevice::LoadAssets()
    {
        // ルートシグネチャー
        {
            //SrvとSmpとCbv付ルートシグネチャ
            m_rootSignature = RootSignature::CreateSrvSmpCbv();
        }
        // 頂点などのリソース構築用のコマンドリスト
        m_commandList = CommandList::CreateSimple(m_commandAllocators[m_frameIndex]);
        //シーンに各オブジェクトの構築を任せる
        App::GetSceneBase().OnInitAssets();
        //コマンドラインクローズおよびキューの実行
        CommandList::Close(m_commandList);
        CommandList::Excute(m_commandQueue, m_commandList);
        //同期オブジェクトおよびGPUの処理待ち
        SyncAndWaitForGpu();
    }
 赤くなっている部分がSimpleSample101と違うところです。

ルートシグネチャの設定

 SrvとSmpとCbv付ルートシグネチャというのはシェーダリソースビュー、サンプラービュー、そしてコンスタントバッファビューという意味です。
 ルートシグネチャは、シェーダーに流すデータの種類と数をあらかじめ定義するものです。
 SimpleSample101の場合は頂点データしか渡さなかったので、シンプルなものでしたが、今回は上記の3種類を渡します。
 RootSignature::CreateSrvSmpCbv()関数は以下になります。
namespace RootSignature {
    static inline ComPtr<ID3D12RootSignature> CreateDirect(const CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC& desc) {
        auto device = App::GetID3D12Device();
        D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {};
        featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1;
        if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &featureData, sizeof(featureData))))
        {
            featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0;
        }
        ComPtr<ID3DBlob> signature;
        ComPtr<ID3DBlob> error;
        ComPtr<ID3D12RootSignature> ret;
        ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&desc, featureData.HighestVersion, &signature, &error),
            L"ルートシグネチャのシリアライズに失敗しました",
            L"D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error)",
            L"RootSignature::CreateDirect()"
            );
        ThrowIfFailed(
            device->CreateRootSignature(0, signature->GetBufferPointer(),
                signature->GetBufferSize(), IID_PPV_ARGS(&ret)),
            L"ルートシグネチャの作成に失敗しました",
            L"device->CreateRootSignature()",
            L"RootSignature::CreateDirect()"
            );
        return ret;
    }
//中略
    //シェーダリソースとサンプラーとコンスタントバッファ
    static inline ComPtr<ID3D12RootSignature> CreateSrvSmpCbv() {

        CD3DX12_DESCRIPTOR_RANGE1 ranges[3];
        ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC);
        ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0);
        ranges[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC);

        CD3DX12_ROOT_PARAMETER1 rootParameters[3];
        rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
        rootParameters[1].InitAsDescriptorTable(1, &ranges[1], D3D12_SHADER_VISIBILITY_PIXEL);
        rootParameters[2].InitAsDescriptorTable(1, &ranges[2], D3D12_SHADER_VISIBILITY_ALL);

        CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
        rootSignatureDesc.Init_1_1(
                _countof(rootParameters), 
                rootParameters, 
                0, 
                nullptr, 
                D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
        );
        return CreateDirect(rootSignatureDesc);
    }
}
 ここでは、どのシェーダーの何番スロットに何を入れるのか、の設定を行います。
 まずデスクプリタレンジですが
    CD3DX12_DESCRIPTOR_RANGE1 ranges[3];
    ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC);
    ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0);
    ranges[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC);
 のようになっています。ここで
    ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC);
 というのは、1つのSRVを0番スロットに、D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATICフラグで設定という意味です。
 これによりシェーダではt0というテクスチャを使用できます。
 同様、サンプラーはs0、コンスタントバッファはb0で取得できます。
 D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATICというのは、ルートシグネチャバージョン1.1から導入されたフラグで、コマンドリストが発行されたあとはディスクプリタヒープに変更がないことを保証するものです。BaseDx12CsvSrvUavは、コマンドリスト発行後は変更しない設計になっているので、このパラメータを使用できます。
 このパラメータは、特にコンスタントバッファについては、高速化につながるようです。
 続く
        CD3DX12_ROOT_PARAMETER1 rootParameters[3];
        rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
        rootParameters[1].InitAsDescriptorTable(1, &ranges[1], D3D12_SHADER_VISIBILITY_PIXEL);
        rootParameters[2].InitAsDescriptorTable(1, &ranges[2], D3D12_SHADER_VISIBILITY_ALL);
 は、rangesの内容を、どのシェーダに送るかを設定します。
        rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
 というのはピクセルシェーダを指し、
        rootParameters[2].InitAsDescriptorTable(1, &ranges[2], D3D12_SHADER_VISIBILITY_ALL);
 というのは全てのシェーダを指します。
 今回のサンプルでは、コンスタントバッファはすべてのシェーダ(ここでは頂点とピクセル)で参照できるようにしています。

各オブジェクトの初期化

 GameDevice::LoadAssets()関数では、このあと
    // 頂点などのリソース構築用のコマンドリスト
    m_commandList = CommandList::CreateSimple(m_commandAllocators[m_frameIndex]);
 とコマンドリストを構築し、
    //シーンに各オブジェクトの構築を任せる
    App::GetSceneBase().OnInitAssets();
 とシーンに初期化をまかせます。その内容は次項で説明します。