TOP > PROGRAM

Bundle

1 はじめに…

今回はD3D12の目玉機能の一つであるBundleの使い方について,一番簡単なサンプルで取り上げてみます。
前回のテクスチャマッピングのサンプルに少し手を加えたものなので画は全然変わっていません。

バンドルを用いた描画
図 1: バンドルを用いた描画

2 Bundleって?

Bundleとは,束という名の通り,小さなコマンドリストを表します。OpenGLを触ったことがある人は,ディスプレイリストのようなものと考えてもらうとわかりやすいかもしれません。Bundleは事前にコマンドを積んでおくことができ,コマンドリストから何度も使用することが可能です。

Bundleを使う際には制限があり,以下を気を付けなければなりません。

Bundle上でこれらのAPIがコールされた場合は,ランタイムはコールを取りやめるという記載がMSDNに書かれています。
そんなわけでBundleを使う際は制限に注意しましょう。

3 使い方

では実際のBundleの使い方ですが,いたってシンプルです。

上のような流れになります。では,実際のコードを見ていきましょう。
今回コマンドリストのラッパークラスを作ってみました。Init()メソッドでコマンドアロケータとコマンドリストを生成します。

//-------------------------------------------------------------------------------------------------
//      初期化処理を行います.
//-------------------------------------------------------------------------------------------------
bool GraphicsCmdList::Init
(
    ID3D12Device*           pDevice,
    D3D12_COMMAND_LIST_TYPE type,
    ID3D12PipelineState*    pipelineState
)
{
    // 引数チェック.
    if ( pDevice == nullptr )
    {
        ELOG( "Error : Invalid Argument." );
        return false;
    }

    // コマンドアロケータを生成.
    auto hr = pDevice->CreateCommandAllocator( type, IID_PPV_ARGS( m_Allocator.GetAddress() ) );
    if ( FAILED( hr ) )
    {
        ELOG( "Error : ID3D12Device::CreateCommandAllocator() Failed." );
        return false;
    }

    // コマンドリストを生成.
    hr = pDevice->CreateCommandList(
        0,
        type,
        m_Allocator.GetPtr(),
        pipelineState,
        IID_PPV_ARGS( m_CmdList.GetAddress() ) );
    if ( FAILED( hr ) )
    {
        ELOG( "Error : ID3D12Device::CreateCommandList() Failed." );
        return false;
    }

    // 正常終了.
    return true;
}

呼び出し元は下記のようになります。

    // バンドルの初期化.
    if ( !m_Bundle.Init( m_pDevice.GetPtr(), D3D12_COMMAND_LIST_TYPE_BUNDLE, m_pPipelineState.GetPtr() ) )
    {
        ELOG( "Error : GraphicsCmdList()::Init() Failed." );
        return false;
    }

続いて,D3D12_COMMAND_LIST_BUNDLE で作ったコマンドリストに描画コマンドを積んでいきます。

    // バンドルにコマンドを積む.
    {
        // ルートシグニチャを設定.
        m_Bundle->SetGraphicsRootSignature( m_pRootSignature.GetPtr() );

        // プリミティブトポロジーの設定.
        m_Bundle->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

        // 頂点バッファビューを設定.
        m_Bundle->IASetVertexBuffers( 0, 1, &m_VertexBufferView );

        // 描画コマンドを生成.
        m_Bundle->DrawInstanced( 6, 1, 0, 0 );

        // バンドルへの記録を終了.
        m_Bundle->Close();
    }

今回はわかりやすいように本当に単純なコマンドのみを積んでいます。
最後に,ExecuteBundle()メソッドを呼び出します。あとはいつも通りです。

//-------------------------------------------------------------------------------------------------
//      描画時の処理です.
//-------------------------------------------------------------------------------------------------
void App::OnRender(FLOAT elapsedSec)
{
    // 回転角を増やす.
    m_RotateAngle += ( elapsedSec / 0.5f );

    // ワールド行列を更新.
    m_ConstantBufferData.World = asdx::Matrix::CreateRotationY( m_RotateAngle );

    // 定数バッファを更新.
    memcpy( m_pCbvDataBegin, &m_ConstantBufferData, sizeof(m_ConstantBufferData) );

    // コマンドアロケータとコマンドリストをリセット.
    m_Immediate.Clear( m_pPipelineState.GetPtr() );

    ID3D12DescriptorHeap* pHeap = m_Heap[DESC_HEAP_BUFFER].GetPtr();

    // ディスクリプタヒープを設定.
    m_Immediate->SetDescriptorHeaps( 1, &pHeap );

    // ルートシグニチャを設定.
    m_Immediate->SetGraphicsRootSignature( m_pRootSignature.GetPtr() );

    // ディスクリプタヒープテーブルを設定.
    auto handleCBV = m_Heap[DESC_HEAP_BUFFER].GetHandleGPU(0);
    auto handleSRV = m_Heap[DESC_HEAP_BUFFER].GetHandleGPU(1);
    m_Immediate->SetGraphicsRootDescriptorTable( 0, handleCBV );
    m_Immediate->SetGraphicsRootDescriptorTable( 1, handleSRV );

    // リソースバリアの設定.
    m_Immediate.Transition(
        m_pRenderTarget[m_FrameIndex].GetPtr(),
        D3D12_RESOURCE_STATE_PRESENT,
        D3D12_RESOURCE_STATE_RENDER_TARGET );

    // ビューポートの設定.
    m_Immediate->RSSetViewports( 1, &m_Viewport );

    // シザー矩形の設定.
    m_Immediate->RSSetScissorRects( 1, &m_ScissorRect );

    // レンダーターゲットのハンドルを取得.
    auto handleRTV = m_Heap[DESC_HEAP_RTV].GetHandleCPU(m_FrameIndex);
    auto handleDSV = m_Heap[DESC_HEAP_DSV].GetHandleCPU(0);

    // レンダーターゲットの設定.
    m_Immediate->OMSetRenderTargets( 1, &handleRTV, FALSE, &handleDSV );

    // レンダーターゲットビューをクリア.
    const FLOAT clearColor[] = { 0.39f, 0.58f, 0.92f, 1.0f };
    m_Immediate->ClearRenderTargetView( handleRTV, clearColor, 0, nullptr );

    // 深度ステンシルビューをクリア.
    m_Immediate->ClearDepthStencilView( handleDSV, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr );

    // バンドルを実行.
    m_Immediate->ExecuteBundle( m_Bundle.GetGfxCmdList() );

    // リソースバリアの設定.
    m_Immediate.Transition(
        m_pRenderTarget[m_FrameIndex].GetPtr(),
        D3D12_RESOURCE_STATE_RENDER_TARGET,
        D3D12_RESOURCE_STATE_PRESENT );

    // コマンドの記録を終了.
    m_Immediate->Close();

    // コマンド実行.
    m_Immediate.Execute( m_pCmdQueue.GetPtr() );

    // 表示する.
    m_pSwapChain->Present( 1, 0 );

    // コマンドの完了を待機.
    WaitForGpu();
}

4 おわりに…

今回は,単純なBundleを使った描画を取り上げてみました。
そろそろ,ポリゴン板でサンプルを作るのもどうかと思ってきたので,次回はモデル表示やろうかと思います。

5 ソースコード

本ソースコードおよびプログラムはMIT Licenseに準じます。
プログラムの作成にはMicrosoft Visual Studio Community 2015, 及び Windows SDK 10.0.10240.0を用いています。
D3D12_Bundle