(作成中)「Optimizing UE4 for Fortnite: Battle Royale – Part 1 | GDC 2018 | Unreal Engine」の日本語メモ
- (作成中)「Optimizing UE4 for Fortnite: Battle Royale – Part 1 | GDC 2018 | Unreal Engine」の日本語メモです.
ストーリーの Save the world からのバトルロワイヤルモードの開発
- バトルロワイヤルモードの追加にかけた時間は 10-12 週
- Save the world モード
- 100+ のAI
- 大量のパーティクルがいろんな方向で発生
- Dedicated server では NavMesh 計算や Behavior を考慮する必要がある
- Battle Royaleモード
- Epic が開発したゲームの中では「もっとマップが広く」「ゲーム中のプレイヤーがもっとも多い」「もっとも多くのプラットフォームに対応した」ゲームになった
- マップサイズは 2.5km x 2.5 km
- 内製ゲームでレベルストリーミングを初めて使った
マルチプラットフォーム対応
- PC, Mac, PS4, Xbox One, iOS, Android(しばらくしたら)
- ゲームのビジュアルをプラットフォームに応じて, スケーリングする
- 使う技術と UE を進化させ続ける
- 60 FPS, レベルストリーミング, モバイルプラットフォーム対応
ゲームスレッド: 問題
- バトルロワイヤルモードで多く人が近くの地点に着陸することもある
- 従って,ハードウェアだけでなくゲームのそういった状況にもスケーラビリティを持つ必要がある
ゲームスレッド : トピック
- Siginifinance Manager
- 動的に変化するコンバットの状況に対応するため
- アニメーション
- 動くコンポーネントについて
- キャラクターの移動について
- スポーン
- チック (Ticking)
- レベルストリーミング
- ゲームの後半で使用した要素
- サーバーパフォーマンス
ゲームスレッド: Significance Manager
- 自分からの距離・スクリーン上のサイズ・可視性に応じて, それぞれのプレイヤーに Significance Bucket(重要性のバケツ, 最高~最低)を割り当てる
- それぞれのバケツごとの最大数を決めていて, それらは調整可能
- これで多くのシステムのスケーラビリティを制御する
- これはエンジン側にあった機能だが, ゲーム側での特有のロジックによって重要性を決めている
Significance Manager の予算
- 重要性のバケツの容量はプラットフォームごとに決めている
- コンソールの場合
- 下図の左から重要度が高い順に: 5, 10, 10, 75 個
- モバイル
- 1(自分自身), 5, 15, 79 個
- CPU リソースが少ないので, こうしている
- Signifiance Manager が制御する最も重要なものが「アニメーション」
アニメーション: キャラクターの組み立て(構成要素)
- 下図が Fornite のバトルロワイヤルでのキャラクターの構成
- 5 個の異なるメッシュ
- 1:ベーススケルトン :ボーンのアニメーション専用でメッシュはなし
- 2:頭のメッシュ
- 3:体のメッシュ
- 4:背中のバックプレーン, バックパックのメッシュ
- 5: 武器のメッシュ
- それぞれが別にアニメーショングラフを持っている
- 但し, ベーススケルトンから一部の情報をコピーして使っている
- さらにその上に物理計算やアニメーションを追加できる
- キャラクターの見た目を変えられるので, 柔軟でクリエイティブな側面がある
- しかし, プレイヤーが 100 人もいるので, 高速化するのが難しい
- 処理するメッシュの数は 100 x 5 = 500 メッシュになる.
- SkeltonMesh LOD はゲームではよくあるトピックで,レンダリング面では役に立つが ゲームスレッド面ではあんまり役に立たない
- ボーンカウントが減るが, 今回はワーカースレッドで処理しているのでゲームスレッドのクリティカルな部分には影響しない
メッシュのマージはしなかった
- 下図の UnrealTournament 3 と同様にメッシュのマージも考えたが, 採用しなかった
- 理由:メモリ使用量の観点から
- マージすると, それぞれのプレイヤーが固有のメッシュを持つ必要が出てくる
- 理由: 柔軟性の観点から
- 見た目の飾りを変えて個性を出せなくなる
アニメーション : 非同期に動かす
- UE4 のアニメーションは 3 ステップ
- 1. Update
- アニメーションに必要なキャラの情報をゲームステートから集める
- 2. Eval
- (処理が重い) 圧縮したアニメーションの解凍とブレンド
- これはメインスレッドとは別のワーカスレッドで処理できる, マルチコアコンソール向け
- 3. Complete
- 状態を, レンダースレッドに描画用に送る
- アタッチメントの更新, Notify を発行する
- 1. Update
- プロファイリングツールを使って確かめながら, Eval がゲームスレッドが離れるようにした
- いくつかバグ(Eval の一部がゲームスレッドで実行されていた)を発見したので,今後のリリースで反映される予定
アニメーションのネイティブ化
- BP はアニメーションも含めて UE4 にとって重要
- BP は柔軟で, Fortnite のいろんな箇所で使っている
- しかし,ネイティブコードに比べると遅い
- 特に毎フレーム行う処理, アニメーションがその一例
- 最初は 約 1 msec かかっていた
- それをゲームプログラマが BP を整理された C++ に書き換えた
- 但し, 柔軟さを保ち, パラメータを外に出すようにしている
- 0.93 msec から 0.19 msec に高速化
- これは正しい方向に進む大きな一歩だった
アニメーション : Fast Path その1
- エンジンの機能として, アニメーション向けの Fast Path を使っていた
- 以下では Fast Path の説明が始まる
- 下図はアニメーショングラフの 1 つで, 赤いノードが個別のアニメーションを再生する「アニメーションのノード」である
- そして, それぞれのアニメーションのノードに対して 再生速度(PlayRate) などのパラメータを入力値として与えている
- この入力値のパラメータはゲームプレイから取得した値に対して, クランプや前処理がされてからアニメーションのノードの入力として使われる値である
- この「入力値のパラメータを加工する処理」を今までは BP でやっていたが, この方法だと最初に「入力値のパラメータを加工するロジックを BP にやらせる必要がある」ため遅かった
- 「稲妻マーク」があるアニメーションノードについては (入力値向けに) 「BP のロジックが実行されないもの」
- 下図では「稲妻マーク」がついたノードは 1 つしかない
アニメーション: Fast Path その1
- そこで今回のプロジェクトではアニメーション再生ノードに下図のような「入力値をスケール, バイアス, クランプ」を追加して, 入力パラメータの加工処理をアニメーション再生ノードの一部として実行できるようにした
- これによって, 入力値のパラメータを「BP で別ロジックで処理」してから渡す必要がなくなった
アニメーション : Fast Path その2
- 下図は上のアニメーション再生のノードのオプションを使ったもの
- ノードに(自分らが望んでいる)「稲妻のマーク」が多く追加された
- これによって, アニメーションの「Eval」の段階を助けることになった
アニメーション : 可能なときはスキップしよう, その1
- 最速のアニメーションは「再生しない」こと
- メッシュが画面上に描画されないときには アニメーションをしないオプションを使っている
- また特定距離より離れた相手の武器のアニメーションを無効化している
- 判定には Significance を使っている
- 特にモバイルでは自分の武器以外はアニメーションさせていない
- しかし, 同じ手法は装備品のバックアップやヘルメットには使えない
- なぜなら,装備の下のキャラクターの本体から(位置や姿勢の)情報をコピーする必要があるため
- 実際にやめた際には, 装備品がキャラクターから浮くことになる
- 実際にそういうバグを少しの期間あったが, 直した
アニメーション : 可能なときはスキップしよう, その2
- プロジェクト向けに 「SkeltalMesh の Static レンダリング」の描画パスを新しく追加した
- 理由
- アイテムは(コピーではなく) 同じメッシュを使って描画したかった
- またアニメーションも不要だし, スキニングの頂点シェーダも使いたくなかった
- 今は Skeltalmesh を Static メッシュとして描画できるので, ジオメトリを最も高速な方法で描画できる
- これをゲーム中で出てくるアイテムに使っている
アニメーション : アップデート頻度の最適化 ( URO: Update Rate Optimization )
- パフォーマンスの問題を解決するための他の役に立つツールを 「Update Rate Optimization (URO)」と呼んでいる
- これは毎フレームアニメーションのロジックを走らせるのではなく, ゲームのフレームレートよりも低い頻度で走らせる
- 一番左
- URO なしのもので, 毎フレームアニメーションを更新している
- 左から 2 番目
- 4 フレームに 1 回アニメーションを更新して, 結果を補間処理をしているもの
- 素晴らしい感じではないが, 結構良く見える
- 近くにいない他のプレイヤーに対してはこれで良さそう
- 左から 3 番目
- 10 フレームに 1 回のもの
- 少し変なダンスみたいな挙動になっている
- 一番右
- 4 フレームに 1 回のアニメーションを更新しているけど, 補間せずに結果をスナップとして使うこともできる
- 補間が不要で, またアタッチメントの更新もしないので, こっちの方が処理が少し軽くなるが動きがやや変
- BOT の一番 Significance が低いものとかで, 本当に必要なときに使っている
- 下は UE4.19.1 のコンソールコマンドです
a.URO.DisableInterpolation Set to 1 to disable interpolation a.URO.Draw True to draw color coded boxes for anim rate. a.URO.Enable True to anim rate optimization. a.URO.ForceAnimRate Non-zero to force anim rate. 10 = eval anim every ten frames for those meshes that can do it. In some cases a frame is considered to be 30fps. a.URO.ForceInterpolation Set to 1 to force interpolation
アニメーション: RigidBody AnimNode(剛体のアニメーションノード)
- キャラクターのアタッチメントの一部では物理計算を使っている
- これには Rigid Body AnimNode を使うようにしている
- 以前の GDC で詳しい話をした
- 単一のノード
- ソルバーが速い
- コリジョン設定のようなオプションがある
- 下図の武器では, コイルの先にゴム製の鮫がくっついていてこれにそのノードを使っている
アニメーションのバウンディングの計算
- アニメーションの処理として, メッシュのバウンディングの計算がある
- 固定サイズのバウンディングを使うことができない
- なぜなら, 下図のようにイケている感情表現でキャラが大きく動くので, 固定サイズのバウンディングボックスだと変なアーティファクトが生じる
- そこで毎フレーム, コリジョン形状を使ってキャラのバウンディングを計算している
- しかし, キャラクターの全てのアタッチメントについては親のメッシュからバウンディングを継承(inherit)するようにしている
- こうすることで全てのキャラクターについて 5 個ではなく, 1 個のバウンディング計算で済む
アニメーション : Notifies(通知)
- アニメーションにイベントをくっつけおき, アニメーションが再生されたときにロジックを発生させる(Fire)
- 例えば, 足音 Notifies , サウンド, パーティクル
- 下図だと振り回している斧の軌跡
- これらは当初は BP で実行していて 柔軟だった
- しかし沢山のプレイヤーが出るゲームでパフォーマンスの心配があったので, BP から C++ を使うように変更した
- Anim Notify というエディタスクリプトを使った
- これについては, このプロジェクトでよく使った方法なので後で話す
- また Async Trace API も使っている
- コリジョントレースをワーカースレッドで行う
- そのフレームですぐに結果がわからずに, 遅れたフレームで結果を取得する
- 床のマテリアルが何か? を調べるために使っている
- あるいは Siginifirance Manager の判定によって, 遠くのキャラの Animation Notifies はスキップできる
Moving Components
- イベントベースのプロファイラをかなり使って, 何が起きているか調べている
- さっき見せたタイムラインビューがそれ
- それぞれのフレームで何が起きているかがわかるので, 調査に役に立つ方法である
- Moving Components の際に何が起きているかを見てみた
- アタッチしている全てのコンポーネントも動かす必要がある
- しかし, 取り除けるコンポーネントは取り除いた
- 例えば 武器のコンポーネントのうち, もう使われていないものやゲームに関係ないものもあった
- そうしたときには 9 個から 3 個に減らすことて改善ができた
- 例えば 武器のコンポーネントのうち, もう使われていないものやゲームに関係ないものもあった
- 「Auto Manage Attachment」というオプションを使った
- コンポーネントが何もしていないときは, そのコンポーネントを動かす必要がないというオプション
- 例えば何もしていないパーティクルシステムは動かす必要がない
- こういった場合にコンポーネントが動かすのはそんなに重くはないがゲームにはプレイヤーが沢山いるので可能な限り負荷は節約したい
- パーティクルシステムにこれを追加したが, オーディオコンポーネントにも追加した
- オーディオコンポーネントが未再生の場合は, 再生するまで移動しない
- UE4.19 で入る機能である
Moving Components : オーバーラップ
- コンポーネット同士が接触して発生するオーバーラップは重い箇所の 1 つ
- デフォルトではオーバーラップのイベントが有効になっているので, プロファイルを見て不要な場合は無効にすること
- 回転しているトマトの頭がゲームであった
- 毎回オーバーラップのイベントを発生しているわけではないが, 動くと発生することがあったので無効化した
- 開けられたドア
- パラシュートで落下する補給物資が, 不必要なオーバーラップイベントを発生させている場合など
- 回転しているトマトの頭がゲームであった
Moving Components : オーバーラップ
- オーバーラップイベントの最適化の話
- アクターの全てのコンポーネントをトラバースして, オーバーラップイベントを発生せるかどうか?の箇所
- 下図はアクター内のコンポーネントのアタッチメントの階層
- 赤はオーバーラップイベントが必要なもの
- 今まではそれぞれをトラーバースしながら, それがオーバーラップイベントが必要かどうか? をチェックする必要があった
- そこで, オーバーラップイベントを必要とする子の数を予め数えておく
- その数が 0 の場合にはその下をチェックせずにスキップする
- メモリは触らない方が(キャッシュが汚れないので)速い
- これも 4.19 で入る最適化の機能
Editor Scripting エディタスクリプト: BP -> Python
- 沢山のコンテンツを変更するときには, エディタスクリプトを使っている
- 今までは BP を使っていたが, しかし今は 実験的に Python をエンジンでサポートしている
- BP でエディタ側にエクスポートしたものは Python にもエクスポートされているので, こういうことは Python でやらせることができる
Editor Scripting : エディタスクリプト
- 使い方としてはScripted Action という BP をエディタで作る
- 対象のアセットをクリックして, 右クリックでそれをアセットに対して走らせるのをエディタ上で行う
- 入力のパラメータを与えてから, そのロジックを走らせることもできる
Editor Scripting : エディタスクリプト
- 下図は Notify を置き換えるために使うもの
- シンプル
- アセットを渡して, For Each Loop を回して, もし AnimSequence であれば, Anim Notify の Replace のユーテリティで Notify を置き換える処理
- 自分たちはエディタの関数(機能)を外に出してシステムで使えるようにするのに,時間をかけるようにしている
- 理由はコンテンツの検査や大量のコンテンツの変更が便利になるから
- 2 つ目の講演では, アーティストがこれら関係でやったことについて話をする
Character Movement : キャラクターの動き
- デフォルトのキャラクターの動きは 「クライアントの動きを完全に予測するもの(Full Client Prediction)」である
- クライアント側で見える全てのキャラクターに対して「Full Client Prediction」を行うのがデフォルト
- これの仕組みとしてはカプセルをスイープさせて, 壁に当たったら止めるもの
- うまく機能するが, 負荷が高い処理
- そこで, 重要度が低いキャラクター(Lower Significance)には低品質で安くキャラクターを動かす方法として「補間」を使う
- サーバーが他のプレイヤーがいる位置を教える
- そして, ( Full Client Prediction ではなく) 位置を補間した結果を使う
- 将来的にはより細かく機能を ON/OFF できるようにする予定
- 例えば, 床の傾きをみて FootIK を使うなど
- また中~低いフレームレートの場合には, 地面との当たりを抑制することができる??( Throttle Ground checks)
Character Movement
- 下図は 2 つの方法を比較している動画
- 下図の上側は「Full Client Prediction」でハイクオリティでより重い
- サーバーからインプットをもらって, 動きをリプレイして 何が起こったかを完全に再現している
- 下図の下側は補間の方
- カプセル形状が周りをテレポートしていることがわかる
- これはサーバーから更新を受け取っている頻度を表している
- スケルトンメッシュのそれぞれの位置に対して補間して動かしている
- ひどい見た目ではないが, 上の方法ほどは良い見た目ではない
- これは重要度の低さ(Lower Significance)に応じてキャラに適用される
- モバイルで に対しては, こっちの方法を多めに使って処理を減らせる
- カプセル形状が周りをテレポートしていることがわかる
以下はまだ作成中…
Spawning
- コンポーネントの数の管理
- BP コンポーネントよりも C++ コンポーネントの方が速い
- 設定(Configure)してから, 登録(Register)
- マテリアルインスタンスのパラメータのコピーの最適化
Ticking
- Slate HUD で 2.6 msec かかっていた
- 最適化で 1.0 msec に減らした
- 遅い BP の削除
- Widgets のキャッシュのために, パネルの Invalidate を追加
- 遅い Widgets の最適化
- さらなる Slate の最適化の計画している
Ticking
- dumpticks enabled のコンソールコマンドで, Tick しているものがダンプされる
- BP を使っている Tick イベント
Ticking
- 時間変化があるので, マテリアルパラメータコレクションを沢山修正している
- 1フレームで1 更新だけするようにシステムを最適化
Ticking : オーディオ
- サウンドキューの Evaluation を減らす
- プラットフォームごとに音源の数を制限している
- PC : 32 個
- Android : 12 個
- iOS : 16 個
- 使用する機能を減らす
- リバーブを使わない
- EQ を使わない
Ticking : テクスチャストリーミング
- ゲームで応答性が下がることがあった
レベルストリーミング
- 2018/1 の Fortnite 2.2.0 でレベルストリーミング機能を追加した
- 何故か?
- メモリ使用量の削減
- マップの多様性を増やすため
- 描画の最適化
- ゲームプレイの最適化
レベルストリーミング : データのロード
- IO
- ワーカースレッド
- デシリアライズ
- 非同期のローディングスレッドにやらせる
- PostLoad
- ゲームスレッドでのタイムスライス
レベルストリーミング:データのロード
- オーバーラップしているレベルをロードする
- IO のビジー状態を保つ
- マテリアルインスタンスのローディングを最適化する
レベルストリーミング: コンポーネントの登録
- 適応型のタイムスライシングを使う
- スカイダイビング時 : 5 msec
- 地上 : 1msec
- ロードに GT(Game Thread?) のあまり時間を使う
レベルストリーミング: コンポーネントの登録
- インスタンスメッシュへの変換機能を追加
- 結局は使わなかった
- でも 4.20 で使用可能になる予定
レベルストリーミング: BeginPlay
- 問題の発見に「イベントプロファイル」を使用
- ウォームアップ(warm up)に時間がかかる虫のパーティクル
- ビルドシステムでの,近傍を探す処理?
サーバのパフォーマンス : Replication
- ~ 50,000 の Replicate されたアクター
- 100 の接続数
- 400+ (以上)のストリーミングレベル
- チックレートは 20 Hz
- ????
- Relevancy Distance の攻め気味の設定
- ???
Dedicate サーバ : Replicated State の共有
- 多くのクライアントに対して,同じ状態を送ることが多い
- 可能なときに「キャッシュや再利用するシステム」を追加した
サーバパフォーマンス : ゲームプレイ
サーバパフォーマンス:アニメーション
何故 60FPS なのか?
- 60 FPS の方が触り心地が良いので
- 入力遅延が少ないので, 触り心地がなめらか
- PS4 Pro と X1X プレイヤーはオプションのアンキャップモードで 40-60 FPS になっていた
- しかし, フレームレートに一貫性はなかったので, 良いゲーム体験ではなかった
- PC でゲームをしているプレイヤーは「見た目」か「フレームレート」の優先度を既につけることができた
- これをコンソールでもできないか?
- 60 FPS の状態をほぼキープして, 見た目もほぼキープできないか?
- PS4 Pro と XBox One X だけでなく, 他の 4 コンソールでもこれができないか?
問題点
- 30FPS のゲームを 60 FPS で走らせるのはいくつかチャレンジがある
- 負荷の予算が 30FPS
- これをどうやってスケールダウンして 16.7 msec にできるのか?
- GPU スケーリング
- 動的解像度変更とスケーラビリティは助けになるが, 全体的に解決するものではない
- CPU スケーリング
- どうやってゲームスレッド(シミュレーション)の負荷を減らせるか?
- どうやって CPU の描画コストを減らすのか?
- 描画コスト: レンダースレッドや並列レンダリングのタスク
60 FPS のユーザ設定
- 60 FPS のゲームオプションを提供したい
- コンソールゲームでは大抵, この設定は固定である
- どうやって動的に 60 FPS モードで有効・無効を切り替えられるか?
- UE4 はデバイスプロファイルのコンフィグファイルをサポートしている
- 特定デバイス向けに設定を上書きするのをゲームに許可する
- 階層的 : 既存の設定を形容して, 特定の設定を上書きできる
- それぞれ 4 コンソール向けに, 60 FPS のデバイスプロファイル
- 60 FPS のデバイスプロファイル
- XBoxOn, XBox One X, PS4, PS4 Pro のデバイスプロファイル
- 設定の上書きが必要
XBox One Base の 60 FPS のデバイスプロファイルのサンプル
プロファイルの手順
- 毎日, 100 人のプレイヤーのプレイヤーテストについて QA チームがプロファイリングデータを集めた
- ベースのパフォーマンスのメトリックス
- 個別のヒッチやスローダウン
- Actionable なパフォーマンスのバグが開発者向けに作られている
- 60 msec 以上のヒッチはバグとみなさせる
- 優先順序付けとして, 「負荷に関するバグ」は「クラッシュバグ」の次点のものとして扱われている
Analytics
テンポラル・アップサンプリング(TAAU: Temporal AA Upsampling)
- TemporalAA(Karis 2014)を拡張して, 今までのフレームバッファのヒストリーを元にアップサンプリングできるようにした
- 去年, Ubisoft や Activision が似たような技術を別の講演で発表し
- TAAU は Spatial アップスケーリングとは違って, 解像度の変更を隠すことができる
テンポラルアップサンプリング: ミップのバイアス
- 自分らがフル解像度で描画しているかのようにテクスチャをサンプリングしたかったので, ミップバイアスが必要だった
- 最終画像において「テクスチャのシャープさ」と「テンポラルなノイズ」のトレードオフが発生する
- 「TextureMipBiasMin と Offset」を見ることで, テクスチャフィルタリングのミップバイアスを制御できる
動的解像度変更
- テンポラルアップサンプリングは解像度の変更を隠蔽する
プレパスと非同期 SSAO
- Fortnite では Z プリパスを行っている
- コンソール変数としては r.EarlyZPass=2, r.EarlyZPassMovable=1
- 利点
- EarlyZPassOnlyMaterialMasking ができるようにしている
- ベースパスではデプステストはデプスイコール
- コンソール変数では r.EarlyZPassOnlyMaterialMasking = 1
- 非同期な SSAO が使える
- 計算には完全なデプスバッファが必要
- サポートしているハードウェアではベースパスと並走できる
- 1msec 以上の節約になる
- EarlyZPassOnlyMaterialMasking ができるようにしている
r.EarlyZPass | Whether to use a depth only pass to initialize Z culling for the base pass. Cannot be changed at runtime. Note: also look at r.EarlyZPassMovable 0: off 1: good occluders only: not masked, and large on screen 2: all opaque (including masked) x: use built in heuristic (default is 3) |
r.EarlyZPassMovable | Whether to render movable objects into the depth only pass. Defaults to on. Note: also look at r.EarlyZPass |
r.EarlyZPassOnlyMaterialMasking | Whether to compute materials’ mask opacity only in early Z pass. Changing this setting requires restarting the editor. Note: Needs r.EarlyZPass == 2 && r.EarlyZPassMovable == 1 |
動的シャドウ
- test
ビジュアルトレードオフ : DFAO
- 60 FPS 向けの XBox One や PS4 の設定では DFAO を無効化
- XBox One では 3msec の 負荷
- しかし, DFAO を無効化すると最初は建物の中が明るくなりすぎた
- SSAO の強度や, スカイライトのインテンシティを下げてつじつまを合わせる
- SSAO は完全ではない
- チープなデプスだけを使う「非同期 SSAO 」は 面のライティング時に問題が出てくる
- SSAO は(スクリーンスペースで計算するものであるため)スクリーンの端っこに対応できない
- DFAO のようにグローバルに計算するものではなく, スクリーンスペースで計算される
頂点の最適化
- 60 FPS を目指す際に, ほとんどのシーンで 頂点ネックになる
- 動的な解像度変更はここでは助けにはならない
- 特に草が重かった : XBox1 では最大 8msec
- コンテンツの最適化のパス
- 全てのメッシュに LOD を追加
- Foliage のマテリアルの頂点シェーダの命令数を半分に
- カメラから遠い Foliage の密度を減らす
- 見た目がほとんど変わらないけど, GPU 負荷を 70% 減らすことができた
CPU のパフォーマンスの課題
- レンダリングの負荷と比較すると, ゲームスレッドの負荷の方が予測しにくい
- 特に多くのプレイヤーが近くにいるとき
- シーンをたどるコスト : カリングなど
- シーン内のオブジェクトの総数でスケールする
- 見える距離が長いので, ドローコール数がかなり増える
- コンソールの RHI において, 一部のリソースの更新にかなりコストがかかる
HLOD
- レンダースレッド(と GPU)の最適化
- 「複数メッシュやマテリアルを 1 つに自動でまとめる」ことで,遠くから見た際のドローコールを減らす
- 目的: スクリーン上でドローコールが大まかに一定にしたい
- Tech Art の講演で詳しい話をします
InitViews の最適化
- InitViews(フラスタムカリング, フレームのセットアップの最初)がパラレルレンダリングでボトルネックになりやすい
- どんなレンダリングジョブを開始する前でも, 5-6 msec の仕事をする必要がある
- プリパスをする必要な準備だけをしてから, プリパスのジョブを開始
- それから, 他のものを処理する
- r.DoInitViewsLightingAfterPrepass
- フレームの最初でプレパスのキックオフをすることができる
- より並列化を活用できる
- レベルストリミーング
- ロードされていないメッシュは考慮する必要がなくなる
描画関係の CPU の最適化
- UE4 のレンダラーは PSO と Bound Shader State をキャッシュする
- スコープロック(クリティカルセクション) で読み書きが保護されている
- UE4.18 後から, プロファイル上でこのロックが目立つようになってロックの取り合いが発生した
- このクリティカルセクションを リード/ライト(Read/Write)ロックに変更することで, 同時にリード(read)できるようになった
- 書き込みの頻度は少なかったので, リソースの取り合いが最小化した
- CPU ネックになっているときには 2msec の負荷削減ができた
RHI の最適化 : XBox One の場合
- UE4 のレンダリングはパイプライン化されている
- レンダリングスレッドは
- ローレベルの描画コマンドを作成する別スレッド(RHI スレッド)
- や
- 追加の描画タスク用のスレッド
- を作る
- レンダリングスレッドは
- レンダリングスレッドからリソースをアップデートするとき, スレッドセーフを保証するために RHI スレッドがストールする
- (例) シェーダリソースビューの更新, 頂点バッファの更新, テクスチャストリーミング
- 一部のケースだとストールが問題になる( 1-3 msec )
- 最悪のケースだと, RHI スレッドがフラッシュする必要がある.
- RHI のコマンドが実行されるまで, レンダースレッドが続きを処理するため待つ必要がある
- RHI リソースの更新の処理をリファクタリングすることで, ストールやフラッシュなしに更新を並列にできるようになった
- 並列性が向上した
プロファイルベースによる最適化の流れ
- hoge