UNREAL FEST 2017 横浜のセッション
「ドラゴンクエストXIへの道~The “L”oad to DRAGONQUEST~」
の講演内容のメモ。
講演者:SQEX 紙山 満さん(プログラマ)
1.UE4を使うにあたって
エンジンでできないことがあったらどうしよう
UE4が無料になる前で検索しても情報が全然でてこない時期だった
これからどんと機能が拡張されるであろう時期だった
何ができて何ができないのかまだまだ手探り状態だった
改造すればなんだってできるが、エンジンに手を入れると‥
・パフォーマンス面での問題
・EpiGamesさんからのサポート面での問題
不具合が改造部分だったり‥
・バージョンアップ時のコンフリクト問題
一番問題なのがこちら ソースごとリファクタリングで無くなったり
バージョンアップで時間がとられる
なるべくエンジンの流儀にしたがって手を入れない
PJ立ち上げ時に堀井さんにご相談
例えば船 マップの端と端がループする世界
このような処理もUE4使うとできなくなるかも知れませんよと
物理、揺れ物、GPUパーティクル出してるかも知れない
これらも一緒に位置を移動できるのか分からなかったので
「アイデアで乗り切ろう」と堀井さん
ファミコン時代からそういうことはいっぱいあった
「霧でも出してごまかせばいいんじゃないの?」みたいな話をしていた
実際に直面して、本当に霧を出してごまかした
エンジンいじるの禁止!
・原則でいじるの禁止
・バージョンロックするまではダメ
・拡張を保証している部分やプラグインは例外
シーケンサーが登場した際のマチネーも
・仕様しているミドルウェアがバリバリいじってるけど
それはメーカーさんが頑張っているということで
Enlightenなど エンジン直接いじったりしている
Enlighten出るまでに1か月
レンダリングをいじってるバージョンアップだと1か月半かかったり
ゲームづくりに集中しましょう
‥とは言えやっぱりそのままだと色々と問題はでてきました
DQ11のマップの作り方は
Area BにクランクがあってArea Aを捨ててArea Cを読み込むというようなよくある読み込み
UE4のレベルストリーミングを使用
モックでは問題なかったが
マップが出来上がってくると、マップを歩いているだけでカクつく
Epicさんに聞いてみたところ‥
「内製ゲームでは困っていない」
アセット数が全然違った Paragon DQ11に比べてアセット数が1/8しかなかった
再現してもらうためにC++使わないサンプルアセットに落とし込んで提出するのは大変
⇒ DQ11一式をシェアしてともに問題を解決していくことに
どのバージョンで改善を?
エンジンロックをいつにするか問題
バージョンアップの度に発生するコストが馬鹿にならない
ブランチしてバージョン上げてコンフリクトなど対応して
少人数で検証 1~2週間かかる
リリースすると全員で触ってそこで初めて起きる問題も出てくる
エンバグすることもある ゲームの挙動が変わったりなどで
UE4アップデートスパンについて
2~3か月に1回バージョンアップしてきている
最新の機能とともに高速化も期待できる
できるだけギリギリまでバージョンアップしたい
でもどこかでやめないとコストが発生する‥
UE4.13でFIXすることにした 本当はもうちょっと先までいきたかった
11発売は2017年7月 マスターは6月
昨年末にアセットを占めてQA開始しているので4.14を待つのは厳しい
PS4SDKバージョンも気にしないといけない SDKの絡みで問題になることもある
UE4.13LoadTimesという専用ブランチ上で問題点を解決していくことに
去年の9月頃からGW明けまでサブミットが続いていた
UE4は1アセット1ファイル
・大量のファイル数になる
・16万アセット
・1つのサブレベルに1000アセットが紐づいている
アセット参照
複数の別アセットを参照している
ハード参照されているアセットは芋蔓式に勝手に読み込まれる仕組み
サブレベルは分かりやすい
ロードがなぜ遅かったか
とあるアセットがあった時 アセットを見つけてロード
少量のものをちょこちょこ > シリアライズ 再帰的に処理
この後読まないといけないファイルが予測しづらい設計になっている
ハードディスクとCPUがあまり並列で動かない処理になっていた
EDLイベントドリブンローダー UE4.15 から使える
クック時に参照するアセットを固めて なる早で読む
昇順キャッシャーという読み込みのキューに投げる
次々と何百というファイルがリクエストされる
キャッシャーが裏で先に読む ここで一工夫
ハードディスクの物理的に昇順に並び変えながら読む
物理的な距離をできるだけ詰めて読み込みを早くする
終わるとコールバックが来てそれを使って初期化する
イベント的に処理をする機構になっている
UE4がディスクレイアウトの昇順を知る必要があるのでUnrealPak必須
使用前、使用後 2~3割のHDD稼働率が8~9割になった
8~9割をできれば10割にしたいとEpicさんに話した
簡単に解決する方法として細かい複数ファイルを1パックにする方法がある
マテリアルインスタンスなど 数KBのものが何百とある
重複パック
メリット
・HDDパフォーマンスは最大になる
・実装は簡単 UEも昔はそうしていた EDLは実装は大変
Epic的には最後の手段にしたい
デメリット
・複数のパックに重複して入るアセットがディスク容量を圧迫
・重複が多いと逆にロード時間が長くなり本末転倒
本当か?試してみた
合計100MBをread
1回で読んだ場合、昇順で読んだ場合 OSキャッシュに乗らないように昇順で読み込む
ほぼ速度が変わらず freadの発行回数が問題なのではなかった
PS4においてはHDD シークの距離が致命的ということが分かった
Epicさんの言ってることは大体合っていた
重複パックは
・パックする内容を吟味しないと逆効果
・ユーザーの調整が必要
昇順ソートで100%とは言わないがほぼハードスペックを引き出せている
汎用エンジンの落としどころでは
UnrealPak上のファイルレイアウト
ゲーム起動オプション
-fileopenlog
ログが出る このファイルの並び順通りに並ぶ
ゲームの起動が1~2割高速化 大体のゲームが起動時は同じ順番でファイルを読む
必ず読み込み順が決まっている場合は有効
ロード時にカクつくのが残っていた
ゲームループが回らないこと
1秒間に0.2描画カクつく カクつきを体験できる スリープさせる処理
マップロード時のカクつき
暗転時にはバレないが、非暗転中はバレる ストリーミング中
Epic内製ではレベルストリーミングを使ったゲームを作っていません
※当時 現在はFortniteでも使われています(Epicさんより)
これはやばい
Epicさんが内製で使ってない機能は最適化や保守がされていない可能性がある
レベルストリーミングの流れ
1.アセットのロード
2.アクターやコンポーネントを生成してワールドに投入
3.アクターやコンポーネントをワールドから取り除いては木
4.アセットの吐き
大部分がメインスレッドでの処理
重い部分を見つける
一番お手軽なのは コンソールコマンド DumpHitchs
階層順でどこが重いかのログが出る カクついた時だけ出るログなので結構使える
1~2割は判明 大体は分からない
UE4補油順のプロファイラもある
stat profile
stat stopfile
BP1つ1つ終えるので便利
SONY提供のプロファイラがあってこちらのお方が正確に解析できる 5割は分かる
最終的にはPrintF 重い処理 3ms以上だったら名前を出す
高速化する
遅延させる
NPC 100体以上いる BeginPlayのタイミングで処理する必要はない 遠くにいたりするので
接地など時間がかかる処理は初めて画面に表示するタイミングで処理する ちらして処理
これができない場合もある
別スレッドにする
大体は無理
UE4ではメインスレッドでしか読めないものが多いので
徐々に重いところからマルチスレッドになってきている
非同期読み込みも入ったがデフォルトでFalseになっているのでTrueにしましょう
複数フレームに処理を分散
エンジンの思い処理は大抵そうなっている
そうなっていなくてもEpicさんに相談
フレーム分散前
UE4.13までは
ClearLevelComponentsでレベルないの全てのコンポーネントを一気に解放
ここでカクついていた
時間を覚えておいて 引数で指定した個数(ここでは5個)ずつ解放
1msを超えてたら次のTickで(次のフレームで)行う
そうやってカクつきを抑えた
1フレ未満には分解できない 1コンポーネントが重いとどうしようもない
早めに確認しましょう
お役立ち機能
difine~~ ログで どこが重いか出してくれる 重くないので常にONにしておくと良い
ロード時の対処もシタケドマダカクつく
GCが原因ではないか?
2.GC
非暗転時にGCが走るケース
・一定時間ごと デフォルト設定で60秒
・サブレベルがストリーミングアウトする際
流れ
1. は木可能なオブジェクトを検索する
2.は木可能なオブジェクトの破棄を開始する
1か2のどちらかが重いとカクつく
これらは分散不可能とEpicさんに言われた
PerformReachabilityAnalysisの流れ
まだGCで見つかっていない処理にフラグを立てておく
ルートセット forloopが回る
オブジェクトが持っているUPropartyのオフセットリストを沢山持っているほど二重目のループが回る
到達不能だったら到達済みにした上でこの配列に足す
一重目のループがどんどん伸びていく
ルートセット 再帰的にどんどん行われていく 全てのルートセットが検索されたら残りを破棄
UObjectの数が多い、またはプロパティが多いほど重い
どうやって減らすことができる?
これを使わないとエディタでアクターを配置できないしエディットもできないので利便性とのトレードオフになる
11のUObject ゲーム中 30~35万
この数だとPS4では検索で100ms低dかかってしまう
・30FPS
・1フレーム33ms
・3フレームほど停止する
検索対象外に逃がす
あるタイミングまでに作られたUObjectは検索対象外に逃がす
非常に軽くなる 25万UObject程度逃がすことができた
・=常駐
・それでも検索対象が5~10万
・まだ検索に10ms~20ms程度ある
オブジェクトを追加、参照してしまうと不具合が起こる DevelopmentBuildではアサート出る
ConditionalBeginDestroyの流れ
検索開始 中身非常に重い処理になっていて沢山呼ばれると思い
処理負荷の原因
2万UObjectくらい行われていた
5000UObjectでも
大量に破棄される例1
スキルパネル
開くだけで2万UObjectがあった
凝ったUI
閉じるとGCですごいヒッチが起こる
スキルパネルを閉じた時に明示的にGCを走らせた
大量に破棄される例2
レベルストリーミング
違うマップのアセット裏読み
トリガーボックスで別エリアのアセットを一部読み込む あちこちに配置されている
問題となるケース このエリアに行こうと思ったもののやっぱりやめた場合
裏読みしたものを破棄して別のアセットを裏読みする これがカクつきの原因になっていた
PLが違うと‥
共通するアセットがほぼ無い
キャンセルすると15000UObjectくらい破棄されていた
ジレンマ
裏読みを減らすと暗転ロード時間が増える
調査
破棄に時間がかかったUObjectの種類をランキングで表示
プロパティ系がランキング上位に出ていることが分かった
BPだった
BPのノードが全てUObject
Epicさんにも相談
コンソールコマンド DumpBlueprint? を作ってくれた 4.17から乗っている
BPを沢山使っているところを見つけてくれる
マクロの中にマクロが含まれたような巨大マクロが何度も使用されていたのが原因
マクロはコピペなので
関数に置き換えた
Latentを含むマクロは前後を関数に
75%ほど削減
ちなみに
ブループリントはクック時にC++に変換dけいる!
不具合があったらEpicさんにぜひ報告を
GCをより高速にする検索機能がある UE4.16から入っている ぜひONに
実機上での確認
クックが必要
遅いです
実機でのライブエディットはできない 思想なので受け入れる
とはいえ‥
実機で確認したいことは色々あります
・パフォーマンス
・レベルトリーミング
クックの流れ
・ソースコードのビルド
・Saved Cookedフォルダ削除
・16万アセットをプラットオーム最適化したバイナリに変換
・UnrealPak
・プラットフォーマーパッケージング
・実機にデプロイ
やっと見れます
1マップのクックだけでもできることが多い
・どこでも武器を変更できる
・
・
その他の起動方法
色々な方法があるが4.13では色々不具合があった 現在は解消されているかも
良かったのはエディタ上からのLaunch
ただしEDLが効かない
ナイトリービルドで確認していた
実機でえでいぃっとできるようにしてしまおうという作戦
レベルストリーミングのボリュームは実機で編集できるデバッグメニューを作って調整
大まかに設定した後、エディタに書き戻す
ソースコードはアセットではないのでクック不要
逆をいうとブループリントはアセットです
実機で調整したいものはC++で
あとはもういかにナイトリービルドを高速に作成するか
ビルドマシンはできるだけスペック高く
できれば64MB搭載 SSDもふんだんに使って
DDCなし24時間が5時間まで短縮された
DDCについてサーバーでキャッシュを共有できる
キャッシュの仕組み
DDCの中 超長いファイル名
アセット誰かが編集して生成する度にユニークなキーが書き込まれる セーブする度に書き換わる
誰かが上書きしないような安全な作りになっている
どんどんゴミが増えていくので定期的にクリーンにした方が良い
DDCを共有することで
ナイトリービルドで全キャッシュが夜中時点の細心になる
日中の更新しかキャッシュが作成されない
そのキャッシュも誰かが作れば共有される
まとめ
エンジンをいじるならバージョンロックしてからにしましょう
バージョンロックしてからマスター延期しないようにしましょう
早い段階から実機で定期的に確認しましょう
困ったことがあったらEpicさんに相談しましょう
(でも今できる範囲のことでゲームを作りましょう)
<講演内容メモはここまで>
スミオさんからの補足をペタリ。
紙山さんが紹介したロードなどのプロファイル用defineはPERF_TRACK_DETAILED_ASYNC_STATS
— スミオ (@tempkinder) 2017年10月8日
#ue4fest
GCの削除時のプロファイル用デファイン PROFILE_ConditionalBeginDestroy (ByClassの方はドラクエ専用カスタマイズです。。。) #ue4fest
— スミオ (@tempkinder) 2017年10月8日
こちらの方のメモも併せてお読み頂ければと思います。
ドラゴンクエストXIへの道 ~The "L"oad To DragonQuestXI~ #ue4fest コードや図の説明が多くてかなりスカスカですが pic.twitter.com/INpzNXs0zX
— ようてん 技術書典3 か34 (@youten_redo) 2017年10月8日
つづき pic.twitter.com/Q8MzCjgj0j
— ようてん 技術書典3 か34 (@youten_redo) 2017年10月8日