メモ:UE4のRenderingの呼ばれ方(2)

[前回の整理] FrowardRenderer におけるDrawVisible系メソッド

DrawVisible系のメソッドは、結局のところ、TStaticMeshDrawList::DrawElement()を呼び出す。

template<typename DrawingPolicyType>
void TStaticMeshDrawList<DrawingPolicyType>::DrawElement(
  const FViewInfo& View,
  const FElement& Element,
  uint64 BatchElementMask,
  FDrawingPolicyLink* DrawingPolicyLink,
  bool& bDrawnShared
)

FrowardRendererのBasePassで使用するDrawingPolicyTypeは、FMeshDrawingPolicyが基底の、TBasePassForForwardShadingDrawingPolicy<T>のテンプレート特殊化クラス。

template<typename LightMapPolicyType>
class TBasePassForForwardShadingDrawingPolicy : public FMeshDrawingPolicy

FrowardRendererで使用されているLightMapPolicyTypeは以下の通り。

FNoLightMapPolicy
TLightMapPolicy<LQ_LIGHTMAP>
TDistanceFieldShadowsAndLightMapPolicy<LQ_LIGHTMAP>
FSimpleDirectionalLightAndSHIndirectPolicy

TStaticMeshDrawList::DrawElement()の大まかな流れ

引数のbDrawnSharedは、直前のDrawElement呼び出しで使用されたDrawPolicyLinkが同じものであるかどうかが代入される。
falseの場合は

DrawingPolicyLink->DrawingPolicy.DrawShared()

を呼び出す。

引数のBatchElementMaskに応じて次のメソッドを、必要な回数呼び出す。

DrawingPolicyLink->DrawingPolicy.SetMeshRenderState()
DrawingPolicyLink->DrawingPolicy.DrawMesh()

上記の通り、DrawElement()の実行部分は、DrawingPoilicyに実装されている。
DrawShared()が、端的に言えば、シェーダー/マテリアル切り替え単位の設定部分。
SetMeshRenderState()が、DrawCallごとに設定が必要なUniform/Texture/Stateの設定部分。
DrawMesh()がDrawCallの呼び出し部分となる。

FMeshDrawingPolicyについて

TBasePassForForwardShadingDrawingPolicyの基底クラスである、FMeshDrawingPolicyを見るとDrawCall呼び出しの大まかな流れが分かる。
DrawElement()に関連する下記の3つのメソッドについて見てみる

DrawShared()
FMeshDrawingPolicyのメンバーで保持しているVertexFactoryの
VertexFactory->Set()
を呼び出し、頂点配列をBindしている。
SetMeshRenderState()
引数で指定されたMeshに設定されているフラグと、FMeshDrawingPolicyに設定されているフラグをチェックして、FillMode,CullModeの指定を行っている。
DrawMesh()
引数で受け取った、Mesh,BatchElementIndexより、DrawCallを呼び出す頂点範囲とIndexBufferを示すFMeshBatchElementを取得し、これに基き、実際のDrawCallを呼び出す。

FMeshDrawingPolicyでは、シェーダーの設定は行われておらず、頂点配列のBindとFillMode,CullModeの指定とDrawCallの呼び出しが行われている。
また、同一DrawingPolicy内では、頂点配列は共有されており、これを複数のDrawCallで共有できる仕組みを有していることが分かる。

TBasePassForForwardShadingDrawingPolicyについて

次に、FMeshDrawingPolicyの派生クラスである、TBasePassForForwardShadingDrawingPolicy<LightMapPolicyType>について見てみる。

コンストラクタ
コンストラクタでは、VertexShaderとPixelShader(後述)を取得している。これらは、LightMapPolicyTypeと、RenderTargetのフォーマットで決まる。
DrawShared()
下記メソッドの呼び出し
RHISetBoundShaerState()
VertexShader->SetParameters()
PixelShader->SetParameters()
LightMapPolicy.Set()
SetMeshRenderState()
下記メソッドの呼び出し
LightMapPolicy.SetMesh()
VertexShader->SetMesh()
PixelShader->SetMesh()
FMeshDrawingPolicy::SetMeshRenderState()
DrawMesh()
FMeshDrawingPolicyと同じ

RHISetBoundShaerState()は、IA/VS/PSのBindを行っており、FMeshDrawingPolicyと比較すると、シェーダー関連の設定と、LightMapPolicyによる設定が追加されているのが分かる。

FNoLightMapPolicyについて

ForwardShadingで使用されているLightMapPolicyTypeの中で、一番単純な、FNoLightMapPolicyの、DrawCallに関連するメソッドを見てみる。

Set()
引数で受け取った、FVertexFactory型の、VertexFactory->Set()を呼び出し、VertexFactoryが保持している頂点配列を、Bindしている。
引数で渡されるVertexFactoryは、TBasePassForForwardShadingDrawingPolicy<T>のメンバーのVertexFactoryなので、遡れば、DrawingPolicyLink->DrawingPolicyのVertexFactoryということになる。つまり同一DrwaingPolicyLink内で頂点配列は共有される。
SetMesh()
何もしない。

VertexShaderとPixelShaderについて

TBasePassForForwardShadingDrawingPolicy<LightMapPolicyType>内で宣言されている、VertexShaderとPixelShaderは以下の通り。

TBasePassForForwardShadingVSBaseType<LightMapPolicyType>* VertexShader;
TBasePassForForwardShadingPSBaseType<LightMapPolicyType>* PixelShader;

どちらもFMeshMaterialShaderを基底にしている。

template<typename LightMapPolicyType>
class TBasePassForForwardShadingVSBaseType : public FMeshMaterialShader, public LightMapPolicyType::VertexParametersType

template<typename LightMapPolicyType>
class TBasePassForForwardShadingPSBaseType : public FMeshMaterialShader, public LightMapPolicyType::PixelParametersType

VertexShader, PixelShaderの各インスタンスは、DrawingPolicyのコンストラクト時に、ポインタが取得される。どちらもShaderの実体を保持するが、設定するUniformやTextureの実体は保持しない。
DrwaElement()に関連する以下のメソッドを見てみる。

VertexShader->SetParameters()
以下のメソッドを呼び出す
HeightFogParameters.Set(GetVertexShader(), &View);
FMeshMaterialShader::SetParameters(GetVertexShader(),MaterialRenderProxy,InMaterialResource,View,TextureMode);
PixelShader->SetParameters()
以下のメソッドを呼び出す
FMeshMaterialShader::SetParameters(GetPixelShader(),MaterialRenderProxy,MaterialResource,*View,TextureMode);
このルーチンはVertexShaderで使われたものと同様。
VertexShader->SetMesh()
以下のメソッドを呼び出す
FMeshMaterialShader::SetMesh(GetVertexShader(),VertexFactory,View,Proxy,BatchElement);
PixelShader->SetMesh()
ReflectionCubeMapのBind(必要に応じて)
以下のメソッドを呼び出す
FMeshMaterialShader::SetMesh(GetPixelShader(),VertexFactory,View,Proxy,BatchElement);

上記のことから、TBasePassForForwardShadingDrawingPolicy内で宣言されている、VertexShaderとPixelShaderクラスは、
FMeshMaterialShaderに対して、HeightFog用のパラメータ設定と、ReflectionCubeMapのBindを追加したものと考えられる。

FMeshMaterialShaderについて

シェーダーのパラメータ設定はおおよそ、この基底クラスに集約されている。DrawElement呼び出しからは、以下のメソッドが呼び出される。

FMeshMaterialShader::SetParameters(GetVertexShader(),MaterialRenderProxy,InMaterialResource,View,TextureMode);

FMaterialShader::SetParameters()を呼び出すだけ。FMaterialShader::SetParameters()では、
引数Viewが保持するUniformBufferのBind (View関連パラメータのUniform)
引数MaterialRenderPorxyが保持してるUniformBufferのBind (Material関連パラーメータのUniform)
引数MaterialRenderPorxyがFGuidを保持してるUniformBufferのBind (ParameterCollection(Sceneが実体保持)のUniform)
引数MaterialRenderPorxyが保持してる2DTextureのBind
引数MaterialRenderPorxyが保持してるCubeTextureのBind

以下は条件によって必要に応じて各種設定される
DeferredShading用のTextureの設定(PixelShader向け)
引数Viewが保持するAtomosphere pass用のUniformパラメータの設定
PostProcessパラメータの設定
EyeAdaptation用のTextureの設定
PerlinNoiseGradiantテクスチャの設定
PerlinNoise3Dテクスチャの設定

これ等のリソースの設定位置(BindSlot)は基本的に、FMaterialShaderが保持している。
対して、設定されるリソースの実体は、引数のView/MaterialRenderProxy、もしくはGlobal変数から取得される。

FMeshMaterialShader::SetMesh(GetPixelShader(),VertexFactory,View,Proxy,BatchElement);

引数VertexFactoryに関連するUniform/Textureの設定。
引数BatchElementに格納されているUniformの設定(内容は変換マトリクスなど)
引数Proxy,Viewに格納されているUniformの設定(内容はLODのFading用)

引数VertexFactoryは頂点配列に相当するデータなので、これに対応するUniformやTextureが設定される。
具体的には、SpeedTree/Particle/GPUSkinning/VectorFieldVisualizationなど、UE4のMaterialで保持しないシェーダーリソース情報の設定がここで行われる。

大まかな流れ

UE4_render_draw_overview

だいたいこんな感じだと思われる。