はじめてのメッシュシェーダ


 1.はじめに…
そろそろメッシュシェーダを用いたアセットワークフローに変更したいなと思ったので,やってみます。





 2. 開発環境
手元では,以下の環境で行っています。
  • Windows 10 Home Build 20161 rs_preleasse
  • Windows 10 SDK Build 20161
  • NVIDIA GeForce RTX 2070, Driver Version 451.48
DirectX12 Ultimateに対応したグラフィックスドライバー,Windows 10 20H1以降の環境でないと動作確認できないと思うので注意してください。


 3. メッシュシェーダとは?
ちょっと大きな規模のゲームを作ると,頂点シェーダがネックになることがあったりします。なぜかというとGPUが巨大になってきていて,入力アセンブラハードウェアはDrawをパイプラインにちょっとずつしか流せないとか,プリミティブアセンブラからの出力レートをフルに保つのが難しいとか,力を上手く使いきれるようにするのが困難になってきているという背景があります。
 近年ではGPU駆動レンダリング[Harr 2015; Mishima 2016]が注目を集めています。その名の通り,GPU上で描画する・しないを判定してGPU上でドローコールを発行するという手法です。ただ,この際に問題があります。頂点シェーダが遅いということです。そこで,ピクセル描画に寄与しないポリゴンを頂点シェーダが実行される前にあらかじめ取り除いておけば,頂点シェーダの負荷が軽減できるはずです。これを実現するために,コンピュートシェーダ上でカリングを行う手法というのが提案されています[Wihlidal 2018]。 また,一方でDirectX10以降,ジオメトリシェーダ・ドメインシェーダ・ハルシェーダの追加によりグラフィックスパイプラインは複雑化の一途を辿っています。頂点シェーダも遅いし,他のシェーダを実行すると当然ながら処理が増えるのでその分遅くなります。これまでのグラフィックスパイプラインをこのまま使い続けるのは無理があるように思えます。

そこで,登場するのがメッシュシェーダ(Mesh Shader)による新しいグラフィックスパイプラインです。入力アセンブラはDrawをパイプラインにちょっとずつ流せない問題等があるため廃止され,テッセレータもありません。コンピュートシェーダのように演算が行えます。メッシュシェーダと共に増幅シェーダ(Amplification Shader)も新たに導入されます。増幅シェーダはメッシュシェーダの前に実行され,その名の通りメッシュシェーダの起動を増やすことができます。これによりジオメトリシェーダの様なプリミティブの増減が可能にできます。また,コンピュートシェーダのようにスレッド単位で実行できるので,カリング実装もメッシュシェーダ内で完結することができ,追加のバッファ等が不要になるといったメリットなどもあります。複雑だったシェーダステージも,増幅シェーダ→メッシュシェーダ→ピクセルシェーダの3つになり非常にスッキリします。これからは,メッシュシェーダが主流になっていくことになるでしょう。


 4. ポリゴン描画
さて,メッシュシェーダについて勉強していきましょう。今回は,いきなりメッシュレットなどは使わずに1枚の三角形を描画するという初歩的なプログラムを組み,初期化フローをきちんと抑えることをきっちり学びたいと思います。まぁ、ポリゴン1枚描くのにメッシュシェーダは適さないのですが,使い方を覚えるのが主目的なので,そこはグッと目をつぶりたいと思います。
まずは,描画に使用するシェーダを用意します。手元のVisual Studio 2019だとシェーダコンパイルがメッシュシェーダ対応されていなかったので,メッシュシェーダコンパイルに対応したカスタムビルドルールを作ってみました。

https://github.com/ProjectAsura/dxc_rule

上記のサイトからdxc.props, dxc.targets, dxc.xmlの3ファイルを取得して,プロジェクトファイルと同じディレクトリに配置してください。配置したらソリューションエクスプローラー上から「ビルドの依存順序」>「ビルドのカスタマイズ」を選択し,「既存ファイルの検索」を押して,ファイルダイアログからdxc.targetsを選択してください。選択すると一覧にdxcが現れるようになるので,チェックボックスにチェックを入れてOKボタンを押しダイアログを閉じてください。カスタムビルドルールを使うのが嫌な人は,バッチファイルとかつかって頑張ってコンパイルとかしてください。
バイナリファイルをロードするのが面倒なので,今回は
-Fh %(ProjectDir)..\res\shaders\Compiled\%(Filename).inc -Vn %(Filename)
のオプションを付けて,ヘッダファイルで出力されるようにしてします。 用意するメッシュシェーダですが、下記になります。
00007:  ///////////////////////////////////////////////////////////////////////////////
00008:  // VertexInput structure
00009:  ///////////////////////////////////////////////////////////////////////////////
00010:  struct VertexInput
00011:  {
00012:      float3 Position;    // 頂点座標.
00013:      float4 Color;       // 頂点カラー.
00014:  };
00015:  
00016:  ///////////////////////////////////////////////////////////////////////////////
00017:  // VertexOutput structure
00018:  ///////////////////////////////////////////////////////////////////////////////
00019:  struct VertexOutput
00020:  {
00021:      float4 Position : SV_POSITION;
00022:      float4 Color    : COLOR0;
00023:  };
00024:  
00025:  ///////////////////////////////////////////////////////////////////////////////
00026:  // PrimitiveOutput structure
00027:  ///////////////////////////////////////////////////////////////////////////////
00028:  struct PrimitiveOutput
00029:  {
00030:      uint PrimitiveId : INDEX0;
00031:  };
00032:  
00033:  //-----------------------------------------------------------------------------
00034:  // Resources and Samplers.
00035:  //-----------------------------------------------------------------------------
00036:  StructuredBuffer<VertexInput>   Vertices    : register(t0);
00037:  StructuredBuffer<uint3>         Indices     : register(t1);
00038:  
00039:  //-----------------------------------------------------------------------------
00040:  //      メインエントリーポイントです.
00041:  //-----------------------------------------------------------------------------
00042:  [numthreads(32, 1, 1)]
00043:  [outputtopology("triangle")]    // 新しいアトリビュート "line" または "triangle" が指定できる.
00044:  void main
00045:  (
00046:      uint groupIndex : SV_GroupIndex,
00047:      out vertices VertexOutput verts[3],         // verticesは必須属性.
00048:      out indices uint3 tris[1],                  // indicesも必須属性.
00049:      out primitives PrimitiveOutput prims[1]     // primitivesはオプション属性.
00050:  )
00051:  {
00052:      // スレッドグループの頂点とプリミティブの数を設定.
00053:      SetMeshOutputCounts(3, 1);
00054:  
00055:      if (groupIndex < 1)
00056:      {
00057:          tris[groupIndex] = Indices[groupIndex];         // 頂点インデックスを設定.
00058:          prims[groupIndex].PrimitiveId = groupIndex;     // プリミティブインデックスを設定.
00059:      }
00060:  
00061:      if (groupIndex < 3)
00062:      {
00063:          VertexOutput vout;
00064:          vout.Position   = float4(Vertices[groupIndex].Position, 1.0f);
00065:          vout.Color      = Vertices[groupIndex].Color;
00066:  
00067:          verts[groupIndex] = vout;   // 頂点を出力.
00068:      }
00069:  }
入力アセンブラを使わないので,入力データのセマンティクスが不要になりダイレクトにStructuredBufferなどにアクセスしてデータを取ります。 メッシュシェーダのスレッドグループの最小値が32なので,[numthreads(32, 1, 1)]として設定しています。今回は3個の頂点を処理するので,29の非アクティブなスレッドが発生していますが,学習が目的なので今回は気にせずに無駄は許容ということにします。ちなみに,出力できるプリミティブ数の最大値は256,出力できる頂点の最大数が256,メッシュシェーダの最大スレッド数が128と上限があるので注意してください。このあたりの仕様についてはDirectX Specsに詳しく記載されているので,そちらを見てください。
メッシュシェーダは,頂点出力と頂点インデックス出力が必須となります。これらは47行目あたりからの
00047:      out vertices VertexOutput verts[3],         // verticesは必須属性.
00048:      out indices uint3 tris[1],                  // indicesも必須属性.
00049:      out primitives PrimitiveOutput prims[1]     // primitivesはオプション属性.
で,指定を行っています。「out vertices データ型 仮引数名」「out indices データ型 仮引数名」といった感じにしてください。indicesのデータ型はライン描画する際はuint2に,トライアングル描画する際はuint3に指定します。ライン描画かトライアングル描画するかはoutputtoplogy属性で指定します。上のプログラムだと43行あたりで指定しています。
00043:  [outputtopology("triangle")]    // 新しいアトリビュート "line" または "triangle" が指定できる.
次に,メッシュシェーダ内の処理についてみていきます。シェーダの先頭で,スレッドグループから実際に出力される数を関数で設定しておく必要があります。これは次の関数で行います。
void SetMeshOutputCounts(uint numVertices, uint numPrimitives);
この関数は1シェーダにつき,1回のみコール可能です。第1引数が出力する頂点数で,第2引数が出力するプリミティブ数になります。今回は1個のポリゴンを書きたいので,
00052:      // スレッドグループの頂点とプリミティブの数を設定. この関数の呼び出しは必須.
00053:      SetMeshOutputCounts(3, 1);
と、指定しています。ちなみにSetMeshOutputCounts()の呼び出しを行わなかった場合は,シェーダの先頭でスレッドグループからエクスポートされる頂点とプリミティブの数が0に設定されるというメッシュシェーダの仕様により,メッシュが出力されない挙動となります。
あとの処理は,実際に出力するデータを組み立てていけばいいです。今回はスレッド数を32に設定しているので,SV_GroupIndexに0~31が来ます。これを頂点番号として使用してデータを組み立ています。シェーダでは早期リターンすると正しく動作しないことが多いので,コードは汚くなりますがif文で囲って処理を書くように注意してください。

続いてピクセルシェーダですが,いつも通りに書きます。下記のような感じです。
00007:  ///////////////////////////////////////////////////////////////////////////////
00008:  // VertexOutput structure
00009:  ///////////////////////////////////////////////////////////////////////////////
00010:  struct VertexOutput
00011:  {
00012:      float4 Position : SV_POSITION;
00013:      float4 Color    : COLOR0;
00014:  };
00015:  
00016:  //-----------------------------------------------------------------------------
00017:  //      メインエントリーポイントです.
00018:  //-----------------------------------------------------------------------------
00019:  float4 main(VertexOutput input) : SV_TARGET
00020:  {
00021:      return input.Color;
00022:  }
まぁ、特に説明することも無いですね。

さて、シェーダが用意できたのでCPU側の処理を書きます。メッシュシェーダにすることで変わるところはパイプラインステートの作成とIASetVertexBuffer()を使わない,IASetIndexBuffer()を使わない,DispatchMesh()を呼び出すといったところになります。まずはパイプラインステートの生成からです。メッシュシェーダ用いたパイプラインではD3D12_PIPELINE_STATE_STREAM_DESC構造体を使ってパイプラインステートを生成します。実装コードは次のような感じです。
00742:      // パイプラインステートの生成.
00743:      {
00744:          // ラスタライザーステートの設定.
00745:          D3D12_RASTERIZER_DESC descRS;
00746:          descRS.FillMode                 = D3D12_FILL_MODE_SOLID;
00747:          descRS.CullMode                 = D3D12_CULL_MODE_NONE;
00748:          descRS.FrontCounterClockwise    = FALSE;
00749:          descRS.DepthBias                = D3D12_DEFAULT_DEPTH_BIAS;
00750:          descRS.DepthBiasClamp           = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
00751:          descRS.SlopeScaledDepthBias     = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
00752:          descRS.DepthClipEnable          = TRUE;
00753:          descRS.MultisampleEnable        = FALSE;
00754:          descRS.AntialiasedLineEnable    = FALSE;
00755:          descRS.ForcedSampleCount        = 0;
00756:          descRS.ConservativeRaster       = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
00757:  
00758:          // レンダーターゲットのブレンド設定.
00759:          D3D12_RENDER_TARGET_BLEND_DESC descRTBS = {
00760:              FALSE, FALSE,
00761:              D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
00762:              D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
00763:              D3D12_LOGIC_OP_NOOP,
00764:              D3D12_COLOR_WRITE_ENABLE_ALL
00765:          };
00766:  
00767:          // ブレンドステートの設定.
00768:          D3D12_BLEND_DESC descBS;
00769:          descBS.AlphaToCoverageEnable  = FALSE;
00770:          descBS.IndependentBlendEnable = FALSE;
00771:          for( UINT i=0; i<D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i )
00772:          { descBS.RenderTarget[i] = descRTBS; }
00773:  
00774:          D3D12_SHADER_BYTECODE ms;
00775:          ms.pShaderBytecode  = SimpleMS;
00776:          ms.BytecodeLength   = sizeof(SimpleMS);
00777:  
00778:          D3D12_SHADER_BYTECODE ps;
00779:          ps.pShaderBytecode  = SimplePS;
00780:          ps.BytecodeLength   = sizeof(SimplePS);
00781:  
00782:          D3D12_DEPTH_STENCILOP_DESC descStencil = {};
00783:          descStencil.StencilFailOp       = D3D12_STENCIL_OP_KEEP;
00784:          descStencil.StencilDepthFailOp  = D3D12_STENCIL_OP_KEEP;
00785:          descStencil.StencilPassOp       = D3D12_STENCIL_OP_KEEP;
00786:          descStencil.StencilFunc         = D3D12_COMPARISON_FUNC_ALWAYS;
00787:  
00788:          D3D12_DEPTH_STENCIL_DESC descDSS = {};
00789:          descDSS.DepthEnable        = FALSE;
00790:          descDSS.DepthWriteMask     = D3D12_DEPTH_WRITE_MASK_ALL;
00791:          descDSS.DepthFunc          = D3D12_COMPARISON_FUNC_LESS_EQUAL;
00792:          descDSS.StencilEnable      = FALSE;
00793:          descDSS.StencilReadMask    = D3D12_DEFAULT_STENCIL_READ_MASK;
00794:          descDSS.StencilWriteMask   = D3D12_DEFAULT_STENCIL_WRITE_MASK;
00795:          descDSS.FrontFace          = descStencil;
00796:          descDSS.BackFace           = descStencil;
00797:  
00798:          D3D12_RT_FORMAT_ARRAY rtvFormats = {};
00799:          rtvFormats.NumRenderTargets = 1;
00800:          rtvFormats.RTFormats[0]     = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
00801:  
00802:          DXGI_SAMPLE_DESC descSample = {};
00803:          descSample.Count    = 1;
00804:          descSample.Quality  = 0;
00805:  
00806:          GEOMETRY_PIPELINE_STATE_DESC descGPS;
00807:          descGPS.RootSignature           = m_pRootSignature.GetPtr();
00808:          descGPS.MS                      = ms;
00809:          descGPS.PS                      = ps;
00810:          descGPS.BlendState              = descBS;
00811:          descGPS.RasterizerState         = descRS;
00812:          descGPS.DepthStencilState       = descDSS;
00813:          descGPS.SampleMask              = UINT_MAX;
00814:          descGPS.PrimitiveTopologyType   = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
00815:          descGPS.RTVFormats              = rtvFormats;
00816:          descGPS.DSVFormat               = DXGI_FORMAT_D32_FLOAT;
00817:          descGPS.SampleDesc              = descSample;
00818:          descGPS.NodeMask                = 0;
00819:          descGPS.Flags                   = D3D12_PIPELINE_STATE_FLAG_NONE;
00820:  
00821:          D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = {};
00822:          streamDesc.pPipelineStateSubobjectStream = &descGPS;
00823:          streamDesc.SizeInBytes                   = sizeof(descGPS);
00824:  
00825:          hr = m_pDevice->CreatePipelineState(&streamDesc, IID_PPV_ARGS(m_pPipelineState.GetAddress()));
00826:          if ( FAILED( hr ) )
00827:          {
00828:              ELOG( "Error : ID3D12Device::CreateGraphicsPipelineState() Failed." );
00829:              return false;
00830:          }
00831:      }
上コードの,806行目付近で出てくる構造体は次のように定義しています。
00049:  ///////////////////////////////////////////////////////////////////////////////
00050:  // PSSubObject class
00051:  ///////////////////////////////////////////////////////////////////////////////
00052:  #pragma warning(push)
00053:  #pragma warning(disable : 4324)
00054:  template<typename InnerStructType, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE Type, typename DefaultArg = InnerStructType>
00055:  class alignas(void*) PSSubObject
00056:  {
00057:  public:
00058:      PSSubObject() noexcept
00059:      : m_Type    (Type)
00060:      , m_Inner   (DefaultArg())
00061:      { /* DO_NOTHING /* }
00062:  
00063:      PSSubObject(InnerStructType const& value) noexcept
00064:      : m_Type    (Type)
00065:      , m_Inner   (value)
00066:      { /* DO_NOTHING /* }
00067:  
00068:      PSSubObject& operator = (InnerStructType const& value) noexcept
00069:      {
00070:          m_Type  = Type;
00071:          m_Inner = value;
00072:          return *this;
00073:      }
00074:  
00075:      operator InnerStructType const&() const noexcept 
00076:      { return m_Inner; }
00077:  
00078:      operator InnerStructType&() noexcept 
00079:      { return m_Inner; }
00080:  
00081:      InnerStructType* operator&() noexcept
00082:      { return &m_Inner; }
00083:  
00084:      InnerStructType const* operator&() const noexcept
00085:      { return &m_Inner; }
00086:  
00087:  private:
00088:      D3D12_PIPELINE_STATE_SUBOBJECT_TYPE m_Type;
00089:      InnerStructType                     m_Inner;
00090:  };
00091:  #pragma warning(pop)
00092:  
00093:  using PSS_ROOT_SIGNATURE        = PSSubObject< ID3D12RootSignature*,            D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE >;
00094:  using PSS_AS                    = PSSubObject< D3D12_SHADER_BYTECODE,           D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS >;
00095:  using PSS_MS                    = PSSubObject< D3D12_SHADER_BYTECODE,           D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS >;
00096:  using PSS_PS                    = PSSubObject< D3D12_SHADER_BYTECODE,           D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS >;
00097:  using PSS_BLEND                 = PSSubObject< D3D12_BLEND_DESC,                D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND >;
00098:  using PSS_SAMPLE_MASK           = PSSubObject< UINT,                            D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK >;
00099:  using PSS_RASTERIZER            = PSSubObject< D3D12_RASTERIZER_DESC,           D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER >;
00100:  using PSS_DEPTH_STENCIL         = PSSubObject< D3D12_DEPTH_STENCIL_DESC,        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL >;
00101:  using PSS_RTV_FORMATS           = PSSubObject< D3D12_RT_FORMAT_ARRAY,           D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS >;
00102:  using PSS_DSV_FORMAT            = PSSubObject< DXGI_FORMAT,                     D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT >;
00103:  using PSS_SAMPLE_DESC           = PSSubObject< DXGI_SAMPLE_DESC,                D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC >;
00104:  using PSS_NODE_MASK             = PSSubObject< UINT,                            D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK >;
00105:  using PSS_CACHED_PSO            = PSSubObject< D3D12_CACHED_PIPELINE_STATE,     D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO >;
00106:  using PSS_FLAGS                 = PSSubObject< D3D12_PIPELINE_STATE_FLAGS,      D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS >;
00107:  using PSS_PRIMITIVE_TOPOLOGY    = PSSubObject< D3D12_PRIMITIVE_TOPOLOGY_TYPE,   D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY >;
00108:  
00109:  
00110:  ///////////////////////////////////////////////////////////////////////////////
00111:  // GEOMETRY_PIPELINE_STATE_DESC structure
00112:  ///////////////////////////////////////////////////////////////////////////////
00113:  struct GEOMETRY_PIPELINE_STATE_DESC
00114:  {
00115:      PSS_ROOT_SIGNATURE      RootSignature;
00116:      PSS_AS                  AS;
00117:      PSS_MS                  MS;
00118:      PSS_PS                  PS;
00119:      PSS_BLEND               BlendState;
00120:      PSS_SAMPLE_MASK         SampleMask;
00121:      PSS_RASTERIZER          RasterizerState;
00122:      PSS_DEPTH_STENCIL       DepthStencilState;
00123:      PSS_PRIMITIVE_TOPOLOGY  PrimitiveTopologyType;
00124:      PSS_RTV_FORMATS         RTVFormats;
00125:      PSS_DSV_FORMAT          DSVFormat;
00126:      PSS_SAMPLE_DESC         SampleDesc;
00127:      PSS_NODE_MASK           NodeMask;
00128:      PSS_FLAGS               Flags;
00129:  };
入力レイアウトの指定もいらなくなっていることに注意してください。
GEOMETRY_PIPELINE_STATE_DESCのMSとPSに設定するシェーダは,次のインクルード文で変数が宣言されます。これは前述した「-Fh %(ProjectDir)..\res\shaders\Compiled\%(Filename).inc -Vn %(Filename)」を指定してdxcに渡すことで生成されるファイルになっています。
00016:  // Shaders
00017:  #include "../res/shaders/Compiled/SimpleMS.inc"
00018:  #include "../res/shaders/Compiled/SimplePS.inc"
ルートシグニチャは下記の設定になっています。
00687:      // ルートシグニチャを生成.
00688:      {
00689:          // ルートパラメータの設定.
00690:          D3D12_ROOT_PARAMETER param[2];
00691:          param[0].ParameterType              = D3D12_ROOT_PARAMETER_TYPE_SRV;
00692:          param[0].ShaderVisibility           = D3D12_SHADER_VISIBILITY_MESH;
00693:          param[0].Descriptor.ShaderRegister  = 0;
00694:          param[0].Descriptor.RegisterSpace   = 0;
00695:  
00696:          param[1].ParameterType              = D3D12_ROOT_PARAMETER_TYPE_SRV;
00697:          param[1].ShaderVisibility           = D3D12_SHADER_VISIBILITY_MESH;
00698:          param[1].Descriptor.ShaderRegister  = 1;
00699:          param[1].Descriptor.RegisterSpace   = 0;
00700:  
00701:          // ルートシグニチャの設定.
00702:          D3D12_ROOT_SIGNATURE_DESC desc;
00703:          desc.NumParameters     = 2;
00704:          desc.pParameters       = param;
00705:          desc.NumStaticSamplers = 0;
00706:          desc.pStaticSamplers   = nullptr;
00707:          desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS
00708:                     | D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS
00709:                     | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS
00710:                     | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS
00711:                     | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS
00712:                     | D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS;
00713:  
00714:          asdx::RefPtr<ID3DBlob> pSignature;
00715:          asdx::RefPtr<ID3DBlob> pError;
00716:  
00717:          // シリアライズする.
00718:          hr = D3D12SerializeRootSignature(
00719:              &desc,
00720:              D3D_ROOT_SIGNATURE_VERSION_1,
00721:              pSignature.GetAddress(),
00722:              pError.GetAddress() );
00723:          if ( FAILED( hr ) )
00724:          {
00725:              ELOG( "Error : D3D12SerializeRootSignataure() Failed." );
00726:              return false;
00727:          }
00728:  
00729:          // ルートシグニチャを生成.
00730:          hr = m_pDevice->CreateRootSignature(
00731:              0,
00732:              pSignature->GetBufferPointer(),
00733:              pSignature->GetBufferSize(),
00734:              IID_PPV_ARGS(m_pRootSignature.GetAddress()) );
00735:          if ( FAILED( hr ) )
00736:          {
00737:              ELOG( "Error : ID3D12Device::CreateRootSignature() Failed." );
00738:              return false;
00739:          }
00740:      }
メッシュシェーダ用にD3D12_SHADER_VISIBILITY_MESHが追加されているので,ShaderVisibilityにこれを指定してあげてください。また,入力レイアウトを使わないので,D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUTの指定は不要になります。これでパイプラインステートの作成までが出来るはずです。
 あとは描画に使用する頂点バッファとインデックスバッファを構造化バッファ(StructuredBuffer)として用意しましょう。
00852:      // 頂点バッファの生成.
00853:      {
00854:          // 頂点データ.
00855:          Vertex vertices[] = {
00856:              { {  0.0f,  0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } },
00857:              { {  0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
00858:              { { -0.5f, -0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }
00859:          };
00860:  
00861:          // ヒーププロパティの設定.
00862:          D3D12_HEAP_PROPERTIES prop;
00863:          prop.Type                 = D3D12_HEAP_TYPE_UPLOAD;
00864:          prop.CPUPageProperty      = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
00865:          prop.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
00866:          prop.CreationNodeMask     = 1;
00867:          prop.VisibleNodeMask      = 1;
00868:  
00869:          // リソースの設定.
00870:          D3D12_RESOURCE_DESC desc;
00871:          desc.Dimension          = D3D12_RESOURCE_DIMENSION_BUFFER;
00872:          desc.Alignment          = 0;
00873:          desc.Width              = sizeof(vertices);
00874:          desc.Height             = 1;
00875:          desc.DepthOrArraySize   = 1;
00876:          desc.MipLevels          = 1;
00877:          desc.Format             = DXGI_FORMAT_UNKNOWN;
00878:          desc.SampleDesc.Count   = 1;
00879:          desc.SampleDesc.Quality = 0;
00880:          desc.Layout             = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
00881:          desc.Flags              = D3D12_RESOURCE_FLAG_NONE;
00882:  
00883:          // リソースを生成.
00884:          hr = m_pDevice->CreateCommittedResource(
00885:              &prop,
00886:              D3D12_HEAP_FLAG_NONE,
00887:              &desc,
00888:              D3D12_RESOURCE_STATE_GENERIC_READ,
00889:              nullptr,
00890:              IID_PPV_ARGS(m_pVertexBuffer.GetAddress()) );
00891:          if ( FAILED( hr ) )
00892:          {
00893:              ELOG( "Error : ID3D12Device::CreateCommittedResource() Failed." );
00894:              return false;
00895:          }
00896:  
00897:          // マップする.
00898:          UINT8* pData;
00899:          hr = m_pVertexBuffer->Map( 0, nullptr, reinterpret_cast<void**>( &pData ) );
00900:          if ( FAILED( hr ) )
00901:          {
00902:              ELOG( "Error : ID3D12Resource::Map() Failed." );
00903:              return false;
00904:          }
00905:  
00906:          // 頂点データをコピー.
00907:          memcpy( pData, vertices, sizeof(vertices) );
00908:  
00909:          // アンマップする.
00910:          m_pVertexBuffer->Unmap( 0, nullptr );
00911:  
00912:  
00913:          D3D12_SHADER_RESOURCE_VIEW_DESC viewDesc = {};
00914:          viewDesc.Format = DXGI_FORMAT_UNKNOWN;
00915:          viewDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
00916:          viewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
00917:          viewDesc.Buffer.FirstElement = 0;
00918:          viewDesc.Buffer.NumElements = 3;
00919:          viewDesc.Buffer.StructureByteStride = sizeof(Vertex);
00920:          viewDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
00921:  
00922:          m_pDevice->CreateShaderResourceView(m_pVertexBuffer.GetPtr(), &viewDesc, handleCPU);
00923:  
00924:          m_VerticesSRV.HandleCPU = handleCPU;
00925:          m_VerticesSRV.HandleGPU = handleGPU;
00926:          handleCPU.ptr += m_ResDescriptorSize;
00927:          handleGPU.ptr += m_ResDescriptorSize;
00928:      }
00929:  
00930:      // インデックスバッファ.
00931:      {
00932:          uint32_t indices[] = { 0, 1, 2 };
00933:  
00934:          // ヒーププロパティの設定.
00935:          D3D12_HEAP_PROPERTIES prop;
00936:          prop.Type                 = D3D12_HEAP_TYPE_UPLOAD;
00937:          prop.CPUPageProperty      = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
00938:          prop.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
00939:          prop.CreationNodeMask     = 1;
00940:          prop.VisibleNodeMask      = 1;
00941:  
00942:          // リソースの設定.
00943:          D3D12_RESOURCE_DESC desc;
00944:          desc.Dimension          = D3D12_RESOURCE_DIMENSION_BUFFER;
00945:          desc.Alignment          = 0;
00946:          desc.Width              = sizeof(indices);
00947:          desc.Height             = 1;
00948:          desc.DepthOrArraySize   = 1;
00949:          desc.MipLevels          = 1;
00950:          desc.Format             = DXGI_FORMAT_UNKNOWN;
00951:          desc.SampleDesc.Count   = 1;
00952:          desc.SampleDesc.Quality = 0;
00953:          desc.Layout             = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
00954:          desc.Flags              = D3D12_RESOURCE_FLAG_NONE;
00955:  
00956:          // リソースを生成.
00957:          hr = m_pDevice->CreateCommittedResource(
00958:              &prop,
00959:              D3D12_HEAP_FLAG_NONE,
00960:              &desc,
00961:              D3D12_RESOURCE_STATE_GENERIC_READ,
00962:              nullptr,
00963:              IID_PPV_ARGS(m_pPrimitiveIndexBuffer.GetAddress()) );
00964:          if ( FAILED( hr ) )
00965:          {
00966:              ELOG( "Error : ID3D12Device::CreateCommittedResource() Failed." );
00967:              return false;
00968:          }
00969:  
00970:          // マップする.
00971:          uint8_t* pData;
00972:          hr = m_pPrimitiveIndexBuffer->Map( 0, nullptr, reinterpret_cast<void**>( &pData ) );
00973:          if ( FAILED( hr ) )
00974:          {
00975:              ELOG( "Error : ID3D12Resource::Map() Failed." );
00976:              return false;
00977:          }
00978:  
00979:          // 頂点データをコピー.
00980:          memcpy( pData, indices, sizeof(indices) );
00981:  
00982:          // アンマップする.
00983:          m_pPrimitiveIndexBuffer->Unmap( 0, nullptr );
00984:  
00985:          D3D12_SHADER_RESOURCE_VIEW_DESC viewDesc = {};
00986:          viewDesc.Format                     = DXGI_FORMAT_UNKNOWN;
00987:          viewDesc.ViewDimension              = D3D12_SRV_DIMENSION_BUFFER;
00988:          viewDesc.Shader4ComponentMapping    = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
00989:          viewDesc.Buffer.FirstElement        = 0;
00990:          viewDesc.Buffer.NumElements         = 1;
00991:          viewDesc.Buffer.StructureByteStride = sizeof(uint32_t) * 3;
00992:          viewDesc.Buffer.Flags               = D3D12_BUFFER_SRV_FLAG_NONE;
00993:  
00994:          m_pDevice->CreateShaderResourceView(m_pPrimitiveIndexBuffer.GetPtr(), &viewDesc, handleCPU);
00995:  
00996:          m_PrimitiveIndicesSRV.HandleCPU = handleCPU;
00997:          m_PrimitiveIndicesSRV.HandleGPU = handleGPU;
00998:          handleCPU.ptr += m_ResDescriptorSize;
00999:          handleGPU.ptr += m_ResDescriptorSize;
01000:      }
これも普通に作ればいいだけなので,特に説明もいらないでしょう。
 ここまでで,データの作成が出来ました。あとはコマンドリストに描画コマンドを積みましょう。
01025:  //-------------------------------------------------------------------------------------------------
01026:  //      描画時の処理です.
01027:  //-------------------------------------------------------------------------------------------------
01028:  void App::OnRender(FLOAT elapsedSec)
01029:  {
01030:      // コマンドアロケータとコマンドリストをリセット.
01031:      m_pCmdAllocator->Reset();
01032:      m_pCmdList->Reset( m_pCmdAllocator.GetPtr(), m_pPipelineState.GetPtr() );
01033:  
01034:      // ディスクリプタヒープを設定.
01035:      m_pCmdList->SetDescriptorHeaps( 1, m_pResHeap.GetAddress() );
01036:  
01037:      // ルートシグニチャを設定.
01038:      m_pCmdList->SetGraphicsRootSignature( m_pRootSignature.GetPtr() );
01039:  
01040:      // ディスクリプタヒープテーブルを設定.
01041:      m_pCmdList->SetGraphicsRootShaderResourceView(0, m_pVertexBuffer->GetGPUVirtualAddress());
01042:      m_pCmdList->SetGraphicsRootShaderResourceView(1, m_pPrimitiveIndexBuffer->GetGPUVirtualAddress());
01043:  
01044:      // ビューポートの設定.
01045:      m_pCmdList->RSSetViewports( 1, &m_Viewport );
01046:  
01047:      // シザー矩形の設定.
01048:      m_pCmdList->RSSetScissorRects( 1, &m_ScissorRect );
01049:  
01050:      // リソースバリアの設定.
01051:      // Present ---> RenderTarget
01052:      D3D12_RESOURCE_BARRIER barrier;
01053:      ZeroMemory( &barrier, sizeof(barrier) );
01054:      barrier.Type                    = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
01055:      barrier.Flags                   = D3D12_RESOURCE_BARRIER_FLAG_NONE;
01056:      barrier.Transition.pResource    = m_pRenderTarget[m_FrameIndex].GetPtr();
01057:      barrier.Transition.StateBefore  = D3D12_RESOURCE_STATE_PRESENT;
01058:      barrier.Transition.StateAfter   = D3D12_RESOURCE_STATE_RENDER_TARGET;
01059:      barrier.Transition.Subresource  = D3D12_RESOURCE_BARRIER_FLAG_NONE;
01060:      m_pCmdList->ResourceBarrier( 1, &barrier );
01061:  
01062:      // レンダーターゲットのハンドルを取得.
01063:      auto handleRTV = m_pRtvHeap->GetCPUDescriptorHandleForHeapStart();
01064:      auto handleDSV = m_pDsvHeap->GetCPUDescriptorHandleForHeapStart();
01065:      handleRTV.ptr += ( m_FrameIndex * m_RtvDescriptorSize );
01066:  
01067:      // レンダーターゲットの設定.
01068:      m_pCmdList->OMSetRenderTargets( 1, &handleRTV, FALSE, &handleDSV );
01069:  
01070:      // レンダーターゲットビューをクリア.
01071:      const FLOAT clearColor[] = { 0.39f, 0.58f, 0.92f, 1.0f };
01072:      m_pCmdList->ClearRenderTargetView( handleRTV, clearColor, 0, nullptr );
01073:  
01074:      // 深度ステンシルビューをクリア.
01075:      m_pCmdList->ClearDepthStencilView( handleDSV, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr );
01076:  
01077:      // メッシュ描画.
01078:      m_pCmdList->DispatchMesh(1, 1, 1);
01079:  
01080:      // リソースバリアの設定.
01081:      // RenderTarget ---> Present
01082:      barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
01083:      barrier.Transition.StateAfter  = D3D12_RESOURCE_STATE_PRESENT;
01084:      m_pCmdList->ResourceBarrier( 1, &barrier );
01085:  
01086:      // コマンドの記録を終了.
01087:      m_pCmdList->Close();
01088:  
01089:      // コマンド実行.
01090:      ID3D12CommandList* ppCmdLists[] = { m_pCmdList.GetPtr() };
01091:      m_pCmdQueue->ExecuteCommandLists( _countof(ppCmdLists), ppCmdLists );
01092:  
01093:      // 表示する.
01094:      m_pSwapChain->Present( 1, 0 );
01095:  
01096:      // コマンドの完了を待機.
01097:      WaitForGpu();
01098:  }
頂点バッファとインデックスバッファはSRVとして設定します。そのためIASetIndexBuffer(), IASetVertexBuffers()は使いません。DrawIndex()の代わりにコンピュートシェーダのDispatch()のようにDispatchMesh()をコールします。
01077:      // メッシュ描画.
01078:      m_pCmdList->DispatchMesh(1, 1, 1);
これで終わりです。

問題なければ,画面にポリゴンが表示されるはずです。


今回は,メッシュシェーダを使ったポリゴン描画について簡単な紹介をしました。
本格的な使い方は,時間が出来たら書くことにします。しばしお待ちを!


 5. 参考文献

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