レンダリング スレッドで CPU バウンドの場合、原因は恐らくドローコールが多すぎるためです。これはよくある問題で、アーティストは頻繁にドローコールを組み合わせて負荷を 下げなければなりません (複数のウォールを 1 つのメッシュに合体させるなど)。実際は負荷はいろいろな領域で発生しています。
- レンダリング スレッドはそれぞれのオブジェクトを処理する必要があります (カリング、マテリアル設定、ライティング設定、コリジョン、負荷の更新など)。 マテリアルが複雑なほど、設定負荷が高くなります。
- レンダリング スレッドは、ドローコールごとにステートを設定し (定数バッファ、テクスチャ、インスタンス プロパティ、シェーダー)、実際の API コールを実施するために GPU コマンドを用意する必要があります。 基本のパスのドローコールは、一般的には深度のみのドローコールよりも負荷が高いです。
- DirectX はデータを検証し、その情報をグラフィック カード ドライバーに渡します。
- ドライバー (NVIDIA、AMD、Intel など) はさらに検証し、ハードウェア用にコマンドバッファを作成します。この部分は別のスレッドに分割されることもあります。
stats コマンドを使うと表示される Mesh Draw Calls は、 3D メッシュによって引き起こされたドローコールを表示します。アーティストはこの数を以下の方法で減らすことができます。
- オブジェクト数 (スタティック/ダイナミック メッシュ、メッシュ パーティクル) を削減する
- 表示距離を短くする (シーン キャプチャ アクタまたはオブジェクト別など)
- 表示を調整する (さらにズームアウト表示、同じビューにならないようにオブジェクトを移動)
- シーン キャプチャ アクタを使わないようにする (シーンを再度レンダリングしたり、fps 設定を下げるか、必要な時のみ更新する必要がある)
- 分割スクリーン (モード) を使わないようにする (ビューが 1 つの場合よりも、 CPU バウンド、スケーラビリティ設定のカスタマイズまたはコンテンツをアグレッシブにする必要がある)
- ドローコールごとのエレメントを減らす (複雑なピクセル シェーダーを受け取るマテリアルをまとめるか、単にマテリアル数を減らす、テクスチャをわずかな大きいテクスチャにまとめる - これでマテリアル数が減った場合は LOD モデルをいくつかのエレメントに使用する)。
- カスタム仕様のデプスやシャドウキャスティングなどのメッシュ上の機能を無効にする。
- 光源をシャドウキャストしないように、あるいはバウンディング ボリューム (ビューコーン、減衰半径) をより厳格に変更する。
ハードウェアのインスタンス化がオプションのケースもあります (同じ 3d モデル、同じシェーダー、パラメータは若干変更されるので、ハードウェアがサポートする必要があります)ハードウェアをインスタンス化すると、ドローコール当たりのドライバー オーバーヘッドが大幅に削減されますが、 柔軟性は制限されます。当社では、メッシュ パーティクルと InstancedFoliage に使用します。
![]() |
---|
CONSOLE: stat SceneRendering |
経験に基くと、ハイエンド PC ではフレームあたり何千ものドローコールが可能です (DirectX11, OpenGL)。最新の API (AMD Mantle, DirectX12) はドライバーのオーバーヘッドの処理を試るので、この数を上回ります。 モバイルでは、ドローコール数は百単位 (OpenGL ES2, OpenGL ES3) ですが、その場合でもドライバー オーバーヘッドが大幅に削減されます (Apple Metal)。
ゲームスレッドで CPU バウンドの場合、この問題の原因となっているゲームコードを調べる必要があります (例:ブループリント、レイキャスト、物理、人工知能、メモリ配置)。
![]() |
---|
CONSOLE: stat Game |
CPU プロファイラでのビルドにより、問題の原因となっている関数を見つけやすくなります。
![]() |
---|
CONSOLE: stat DumpFrame -ms=0.1 |
ここでは、出力のカスタマイズに 0.1 ミリ秒のしきい値を使いました。コマンド実行後、結果がログとコンソールに表示されます。 階層には、ミリ秒時間とコール数が表示されます。必要があれば、 QUICK_SCOPE_CYCLE_COUNTER をコードに追加して、以下の例のように階層をきれいにすることができます。
virtual void DrawDynamicElements(FPrimitiveDrawInterface* PDI,const FSceneView* View) override
{
QUICK_SCOPE_CYCLE_COUNTER( STAT_BoxSceneProxy_DrawDynamicElements );
const FMatrix& LocalToWorld = GetLocalToWorld();
const FColor DrawColor = GetSelectionColor(BoxColor, IsSelected(), IsHovered(), false);
DrawOrientedWireBox(PDI, LocalToWorld.GetOrigin(), ... );
}
CPU バウンドでない場合、GPU バウンド です。