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

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

1103.シンプルな三角形の描画(3)

 では、前項に続きましてGameDevice::LoadPipeline()関数の中身を解説したいと思います。

factory

 数々のDx12オブジェクトの中で、factoryはちょっと変わったオブジェクトです。
 factory問う名前の通り、何かを作成するために存在します。その何か、というのはDx12デバイススワップチェーンです。この2つはfactoryを使って構築します。
 factoryの構築は以下の様な記述になっています。
        //ファクトリ
        ComPtr<IDXGIFactory4> factory = Dx12Factory::CreateDirect();
 Dx12Factoryネームスペースとなっており、追いかけていくと以下の記述になります。
    namespace Dx12Factory {
        static inline ComPtr<IDXGIFactory4> CreateDirect() {
            UINT dxgiFactoryFlags = 0;
#if defined(_DEBUG)
            // Enable the debug layer (requires the Graphics Tools "optional feature").
            // NOTE: Enabling the debug layer after device creation will invalidate the active device.
            {
                ComPtr<ID3D12Debug> debugController;
                if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
                {
                    debugController->EnableDebugLayer();

                    // Enable additional debug layers.
                    dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
                }
            }
#endif
            ComPtr<IDXGIFactory4> factory;
            ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
            return factory;
        }
    }
 このように、DirectX-Graphics-Samplesの中のサンプルと同様になっています。
 また、赤くなっているThrowIfFailed()関数について説明します。
 実体は以下のようになっています。これもDirectX-Graphics-Samplesを習ったものです。
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            throw HrException(hr);
        }
    }
 このようにシンプルなものです。Dx12は全体的にCOMという技術を利用します。CreateDXGIFactory2()関数などは、COMインターフェイスを作成します。これらの関数はHRESULTという型を返しますが、これが失敗した時はFAILED(hr)でチェックできる形になります。つまり、関数呼び出しに失敗したら、例外をスローしなさい、という意味になります。
 BaseDx12ではこれをカスタマイズした日本語のエラー文字を送出できるバージョンもあります。以下が実体です。
    inline void ThrowIfFailed(HRESULT hr,
        const std::wstring& wstr1,
        const std::wstring& wstr2 = std::wstring(),
        const std::wstring& wstr3 = std::wstring()
    )
    {
        if (FAILED(hr))
        {
            throw HrException(hr, Util::WStoRetMB(wstr1) + Util::WStoRetMB(wstr2) + Util::WStoRetMB(wstr3));
        }
    }

    inline void ThrowIfFailed(HRESULT hr,
        const std::string& str1,
        const std::string& str2 = std::string(),
        const std::string& str3 = std::string()
    )
    {
        if (FAILED(hr))
        {
            throw HrException(hr, str1 + str2 + str3);
        }
    }
 このようにwstring版string版があります。

デバイス

 これまでのDirectXのバージョンもそうでしたが、Dx12も中心となる重要なオブジェクトはデバイスです。
 多くの命令がこのデバイスを通じて行われます。
 factoryの作成が終わりましたら、続いて作成します。以下がそのブロックです。
        //デバイス
        m_device = D3D12Device::CreateDefault(factory, m_useWarpDevice);
 ここで、先ほど作成したfactoryを渡します。m_useWarpDeviceというのはラップデバイスを使うかどうかです。ハードウェアデバイスのみに対応するのでfaiseとなっています。
 m_deviceというのは、親クラスのDx12Deviceクラスにあるprotected変数です。protectedなので、継承先であるGameDeviceクラスからも、直接扱えます。
 その宣言は以下のようになっています。
        ComPtr<ID3D12Device> m_device;
 このようにID3D12DeviceインターフェイスDx12のデバイスです。
 デバイスの作成は、以下の様なコードが呼ばれます。
        static inline ComPtr<ID3D12Device> CreateDefault(ComPtr<IDXGIFactory4> factory, bool useWarpDevice) {
            ComPtr<ID3D12Device> device;
            if (useWarpDevice)
            {
                ComPtr<IDXGIAdapter> warpAdapter;
                ThrowIfFailed(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)));

                ThrowIfFailed(D3D12CreateDevice(
                    warpAdapter.Get(),
                    D3D_FEATURE_LEVEL_11_0,
                    IID_PPV_ARGS(&device)
                ));
            }
            else
            {
                ComPtr<IDXGIAdapter1> hardwareAdapter;
                GetHardwareAdapter(factory.Get(), &hardwareAdapter);

                ThrowIfFailed(D3D12CreateDevice(
                    hardwareAdapter.Get(),
                    D3D_FEATURE_LEVEL_11_0,
                    IID_PPV_ARGS(&device)
                ));
            }
            return device;
        }
 ラップモードではありませんので、赤い部分が実行されます。
 ここで呼ばれているGetHardwareAdapter()関数はサブ関数で、以下の様な内容です。
_Use_decl_annotations_
void GetHardwareAdapter(IDXGIFactory2* pFactory, IDXGIAdapter1** ppAdapter)
{
    ComPtr<IDXGIAdapter1> adapter;
    *ppAdapter = nullptr;

    for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != pFactory->EnumAdapters1(adapterIndex, &adapter); ++adapterIndex)
    {
        DXGI_ADAPTER_DESC1 desc;
        adapter->GetDesc1(&desc);

        if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
        {
            // Don't select the Basic Render Driver adapter.
            // If you want a software adapter, pass in "/warp" on the command line.
            continue;
        }

        // Check to see if the adapter supports Direct3D 12, but don't create the
        // actual device yet.
        if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
        {
            break;
        }
    }

    *ppAdapter = adapter.Detach();
}
 ここの記述はDirectX-Graphics-Samplesのものとまるで同じですが、この部分をオリジナル化するメリットもありません。
 着実にお決まりの処理をこなしてくれればいいと思います。

 このようにして、作成されたID3D12DeviceインターフェイスD3D12Device::CreateDefault()関数によって作成され、GameDevice::LoadPipeline()関数に戻されます。

 それでは次項ではコマンドキューの作成から続けましょう。