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

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

1104.シンプルな三角形の描画(4)

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

コマンドキュー

 続いてコマンドキューを作成します。
 Dx12コマンドリストという描画命令をまとめたものを積み上げていって、いくつかのタイミングで一気に実行します。
 その際コマンドリストを実行するという役割を果たすのがコマンドキューです。
 コマンドキューは、以下のように作成します。
        //コマンドキュー
        m_commandQueue = CommandQueue::CreateDefault();
 この呼び出しは、以下のようなコードになります。
    namespace CommandQueue {
        static inline ComPtr<ID3D12CommandQueue> CreateDirect(const D3D12_COMMAND_QUEUE_DESC& desc) {
            auto device = App::GetID3D12Device();
            ComPtr<ID3D12CommandQueue> queue;
            ThrowIfFailed(device->CreateCommandQueue(&desc, IID_PPV_ARGS(&queue)));
            return queue;
        }
        static inline ComPtr<ID3D12CommandQueue> CreateDefault() {
            D3D12_COMMAND_QUEUE_DESC queueDesc = {};
            queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
            queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
            return CreateDirect(queueDesc);
        }
    }
 このように、まず、赤くなってるCommandQueue::CreateDefault()が呼び出され、そこから、CreateDirect()関数を呼び出します。
 CreateDirect()関数内では
            ThrowIfFailed(device->CreateCommandQueue(&desc, IID_PPV_ARGS(&queue)));
 と実行して、queueを初期化し、そのままリターンします。
 コマンドキューも特別なパラメータをつける必要もないでしょう(DirectX-Graphics-Samplesと同じで良いと思います)。

スワップチェーン

 続いてスワップチェーンです。GPUはバックバッファに書き込みが終わると、そのバックバッファフロントバッファに交換してプレゼンテーションという処理を行いますが、この処理を行うのがスワップチェーンです。バックバッファは、複数設定でき、このサンプルでは2つ使用しますが、それらに対して、順番に書き込みフロントバッファに移動の処理を、スワッププチェーンは行います。
 スワッププチェーンに渡すパラメータは、3つ、ファクトリ、コマンドキュー、そして、フレーム数です。
        //スワップチェーン
        m_swapChain = SwapChain::CreateDefault(factory,m_commandQueue, m_frameCount);
 のように初期化します。中で行われている処理は、以下の内容です。
namespace SwapChain {
    static inline ComPtr<IDXGISwapChain3> 
    CreateDirect(
        ComPtr<IDXGIFactory4> factory,
        const DXGI_SWAP_CHAIN_DESC1& desc,
        ComPtr<ID3D12CommandQueue> queue
    ) {
        ComPtr<IDXGISwapChain1> swapChain;
        ThrowIfFailed(factory->CreateSwapChainForHwnd(
            queue.Get(),        // Swap chain needs the queue so that it can force a flush on it.
            App::GetHwnd(),
            &desc,
            nullptr,
            nullptr,
            &swapChain
        ));
        //Alt+Enterでフルスクリーンにならない
        ThrowIfFailed(factory->MakeWindowAssociation(App::GetHwnd(), DXGI_MWA_NO_ALT_ENTER));
        ComPtr<IDXGISwapChain3> swapChain3;
        ThrowIfFailed(swapChain.As(&swapChain3));
        return swapChain3;
    }
    static inline ComPtr<IDXGISwapChain3>
    CreateDefault(ComPtr<IDXGIFactory4> factory,ComPtr<ID3D12CommandQueue> queue,UINT framecount) {
        // Describe and create the swap chain.
        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
        swapChainDesc.BufferCount = framecount;
        swapChainDesc.Width = App::GetGameWidth();
        swapChainDesc.Height = App::GetGameHeight();
        swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
        swapChainDesc.SampleDesc.Count = 1;
        return CreateDirect(factory,swapChainDesc, queue);
    }
}
 赤くなっているところが、GameDevice::LoadPipeline()から呼ばれる関数です。
 この中ではスワップチェーンを作成するために、まとめておく構造体DXGI_SWAP_CHAIN_DESC1を定義します。
        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
 で、内容を0にします。
        swapChainDesc.BufferCount = framecount;
 はフレーム数の設定ですね。このサンプルでは2です
        swapChainDesc.Width = App::GetGameWidth();
        swapChainDesc.Height = App::GetGameHeight();
 は、ウインドウの大きさです。Appクラスに登録されているので持ってきます。
        swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
 は、RGBA(Red、Green、Blue、Alpha)の、各8ビット正規化整数形式、です。
        swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 は、スワップチェーンがレンダーターゲット出力用として使用する設定です。
        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
 はプレゼントした後に、バックバッファの内容を破棄するよう設定します。
        swapChainDesc.SampleDesc.Count = 1;
 ピクセルごとのマルチサンプルの数で、ここでは、1を指定しています。
 ここに出てきてない設定は、
        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
 で0に初期化されていますので、0falseふが設定されています。

 設定が終わったら、
        return CreateDirect(factory,swapChainDesc, queue);
 とCreateDirect()関数を、定義済み構造体を渡して呼び出し、その戻り値を直接返します。

 CreateDirect()関数では、まず、ウィンドウのハンドルをもとにスワップチェーンを作成します。
        ComPtr<IDXGISwapChain1> swapChain;
        ThrowIfFailed(factory->CreateSwapChainForHwnd(
            queue.Get(),        // Swap chain needs the queue so that it can force a flush on it.
            App::GetHwnd(),
            &desc,
            nullptr,
            nullptr,
            &swapChain
        ));

 ただここで作成されるのはIDXGISwapChain1インターフェイスです。
 サンプルで使用するのはIDXGISwapChain3インターフェイスなので、もう少し作業があります。
 まず、Alt+Enterでフルスクリーンにならないようにします。DirectXは伝統的にAlt+Enterでフルスクリーンに切り替えができますが、その機能はできなくします。
 ただしその設定はIDXGISwapChain1インターフェイスを使って設定しますので
        //Alt+Enterでフルスクリーンにならない
        ThrowIfFailed(factory->MakeWindowAssociation(App::GetHwnd(), DXGI_MWA_NO_ALT_ENTER));
 を実行します。こうするとAlt+Enterでフルスクリーンにならない設定になります。
 最後に
        ComPtr<IDXGISwapChain3> swapChain3;
        ThrowIfFailed(swapChain.As(&swapChain3));
        return swapChain3;
 とIDXGISwapChain3インターフェイスにバージョンアップして、それをリターンします。
 このようにしてIDXGISwapChain3インターフェイスの作成が終わります。

 GameDevice::LoadPipeline()ではこの戻り値をm_swapChainに代入します。
 その後
        //フレームインデックスの初期値
        m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
 とフレームのインデックスを設定します。この値は、m_swapChain経由で取得します。

 次項では、デスクプリタヒープの設定をします。
 デスクプリタヒープは、Dx12の中でも、1,2を争うよくわからないオブジェクトだと思います。
 しかし、細かく見ていけば、その存在理由もわかってくると思います。