ゲームエフェクトデザイナーのブログ (新)

レポート記事とかUE4のマテリアルとか。C#とかも触ったり。

「ドラゴンクエストXIへの道~The “L”oad to DRAGONQUEST~」の講演メモ

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さんに相談しましょう
(でも今できる範囲のことでゲームを作りましょう)

<講演内容メモはここまで>

スミオさんからの補足をペタリ。

こちらの方のメモも併せてお読み頂ければと思います。