Bundle


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




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

Bundleを使う際には制限があり,以下を気を付けなければなりません。
* Bundleによって参照される全てのパイプラインステートオブジェクトは,レンダーターゲットのフォーマット,深度バッファのフォーマット,サンプルがそれぞれ同一でなければなりません。
* D3D12_COMMAND_LIST_TYPE_BUNDLE で生成されたコマンドリスト上では,以下のコマンドリストAPIコールは許可されません。
  • すべての Clear メソッド
  • すべての Copy メソッド
  • DiscardResource
  • ExecuteBundle
  • ReosolveSubresource
  • SetPredication
  • BeginQuery
  • EndQuery
  • SOSetTargets
  • OMSetRenderTargets
  • RSSetViewports
  • RSSetScissorRects
* SetDescriptorHeap はBundle上で呼び出し可能ですが,バンドルディスクリプタヒープは呼び出し元のコマンドリストディスクリプタヒープと一致する必要があります。

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


 3. 使い方
では実際のBundleの使い方ですが,いたってシンプルです。
(1) D3D12_COMMAND_LIST_TYPE_BUNDLE でコマンドアロケータとコマンドリストを生成。
(2) D3D12_COMMAND_LIST_TYPE_BUNDLE で作ったコマンドリストにコマンドを積んでいく。
(3) ID3D12GraphicsCommandList::ExecuteBundle() でメインのコマンドリストにBundlenのコマンドを積む。
上のような流れになります。では,実際のコードを見ていきましょう。
今回コマンドリストのラッパークラスを作ってみました。Init()メソッドでコマンドアロケータとコマンドリストを生成します。
00034:  //-------------------------------------------------------------------------------------------------
00035:  //      初期化処理を行います.
00036:  //-------------------------------------------------------------------------------------------------
00037:  bool GraphicsCmdList::Init
00038:  (
00039:      ID3D12Device*           pDevice,
00040:      D3D12_COMMAND_LIST_TYPE type,
00041:      ID3D12PipelineState*    pipelineState
00042:  )
00043:  {
00044:      // 引数チェック.
00045:      if ( pDevice == nullptr )
00046:      {
00047:          ELOG( "Error : Invalid Argument." );
00048:          return false;
00049:      }
00050:  
00051:      // コマンドアロケータを生成.
00052:      auto hr = pDevice->CreateCommandAllocator( type, IID_PPV_ARGS( m_Allocator.GetAddress() ) );
00053:      if ( FAILED( hr ) )
00054:      {
00055:          ELOG( "Error : ID3D12Device::CreateCommandAllocator() Failed." );
00056:          return false;
00057:      }
00058:  
00059:      // コマンドリストを生成.
00060:      hr = pDevice->CreateCommandList(
00061:          0,
00062:          type,
00063:          m_Allocator.GetPtr(),
00064:          pipelineState,
00065:          IID_PPV_ARGS( m_CmdList.GetAddress() ) );
00066:      if ( FAILED( hr ) )
00067:      {
00068:          ELOG( "Error : ID3D12Device::CreateCommandList() Failed." );
00069:          return false;
00070:      }
00071:  
00072:      // 正常終了.
00073:      return true;
00074:  }
呼び出し元は下記のようになります。
00809:      // バンドルの初期化.
00810:      if ( !m_Bundle.Init( m_pDevice.GetPtr(), D3D12_COMMAND_LIST_TYPE_BUNDLE, m_pPipelineState.GetPtr() ) )
00811:      {
00812:          ELOG( "Error : GraphicsCmdList()::Init() Failed." );
00813:          return false;
00814:      }
続いて,D3D12_COMMAND_LIST_BUNDLE で作ったコマンドリストに描画コマンドを積んでいきます。
00816:      // バンドルにコマンドを積む.
00817:      {
00818:          // ルートシグニチャを設定.
00819:          m_Bundle->SetGraphicsRootSignature( m_pRootSignature.GetPtr() );
00820:  
00821:          // プリミティブトポロジーの設定.
00822:          m_Bundle->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
00823:  
00824:          // 頂点バッファビューを設定.
00825:          m_Bundle->IASetVertexBuffers( 0, 1, &m_VertexBufferView );
00826:  
00827:          // 描画コマンドを生成.
00828:          m_Bundle->DrawInstanced( 6, 1, 0, 0 );
00829:  
00830:          // バンドルへの記録を終了.
00831:          m_Bundle->Close();
00832:      }
今回はわかりやすいように本当に単純なコマンドのみを積んでいます。
最後に,ExecuteBundle()メソッドを呼び出します。あとはいつも通りです。
00860:  //-------------------------------------------------------------------------------------------------
00861:  //      描画時の処理です.
00862:  //-------------------------------------------------------------------------------------------------
00863:  void App::OnRender(FLOAT elapsedSec)
00864:  {
00865:      // 回転角を増やす.
00866:      m_RotateAngle += ( elapsedSec / 0.5f );
00867:  
00868:      // ワールド行列を更新.
00869:      m_ConstantBufferData.World = asdx::Matrix::CreateRotationY( m_RotateAngle );
00870:  
00871:      // 定数バッファを更新.
00872:      memcpy( m_pCbvDataBegin, &m_ConstantBufferData, sizeof(m_ConstantBufferData) );
00873:  
00874:      // コマンドアロケータとコマンドリストをリセット.
00875:      m_Immediate.Clear( m_pPipelineState.GetPtr() );
00876:  
00877:      ID3D12DescriptorHeap* pHeap = m_Heap[DESC_HEAP_BUFFER].GetPtr();
00878:  
00879:      // ディスクリプタヒープを設定.
00880:      m_Immediate->SetDescriptorHeaps( 1, &pHeap );
00881:  
00882:      // ルートシグニチャを設定.
00883:      m_Immediate->SetGraphicsRootSignature( m_pRootSignature.GetPtr() );
00884:  
00885:      // ディスクリプタヒープテーブルを設定.
00886:      auto handleCBV = m_Heap[DESC_HEAP_BUFFER].GetHandleGPU(0);
00887:      auto handleSRV = m_Heap[DESC_HEAP_BUFFER].GetHandleGPU(1);
00888:      m_Immediate->SetGraphicsRootDescriptorTable( 0, handleCBV );
00889:      m_Immediate->SetGraphicsRootDescriptorTable( 1, handleSRV );
00890:  
00891:      // リソースバリアの設定.
00892:      m_Immediate.Transition(
00893:          m_pRenderTarget[m_FrameIndex].GetPtr(),
00894:          D3D12_RESOURCE_STATE_PRESENT,
00895:          D3D12_RESOURCE_STATE_RENDER_TARGET );
00896:  
00897:      // ビューポートの設定.
00898:      m_Immediate->RSSetViewports( 1, &m_Viewport );
00899:  
00900:      // シザー矩形の設定.
00901:      m_Immediate->RSSetScissorRects( 1, &m_ScissorRect );
00902:  
00903:      // レンダーターゲットのハンドルを取得.
00904:      auto handleRTV = m_Heap[DESC_HEAP_RTV].GetHandleCPU(m_FrameIndex);
00905:      auto handleDSV = m_Heap[DESC_HEAP_DSV].GetHandleCPU(0);
00906:  
00907:      // レンダーターゲットの設定.
00908:      m_Immediate->OMSetRenderTargets( 1, &handleRTV, FALSE, &handleDSV );
00909:  
00910:      // レンダーターゲットビューをクリア.
00911:      const FLOAT clearColor[] = { 0.39f, 0.58f, 0.92f, 1.0f };
00912:      m_Immediate->ClearRenderTargetView( handleRTV, clearColor, 0, nullptr );
00913:  
00914:      // 深度ステンシルビューをクリア.
00915:      m_Immediate->ClearDepthStencilView( handleDSV, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr );
00916:  
00917:      // バンドルを実行.
00918:      m_Immediate->ExecuteBundle( m_Bundle.GetGfxCmdList() );
00919:  
00920:      // リソースバリアの設定.
00921:      m_Immediate.Transition(
00922:          m_pRenderTarget[m_FrameIndex].GetPtr(),
00923:          D3D12_RESOURCE_STATE_RENDER_TARGET,
00924:          D3D12_RESOURCE_STATE_PRESENT );
00925:  
00926:      // コマンドの記録を終了.
00927:      m_Immediate->Close();
00928:  
00929:      // コマンド実行.
00930:      m_Immediate.Execute( m_pCmdQueue.GetPtr() );
00931:  
00932:      // 表示する.
00933:      m_pSwapChain->Present( 1, 0 );
00934:  
00935:      // コマンドの完了を待機.
00936:      WaitForGpu();
00937:  }


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


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