Thriveのためのカスタム植生システムでパフォーマンスを最適化:重い王冠

Zugalu Entertainmentは2014年に、革新、ノスタルジア、商業的魅力を融合させたゲームを作るために設立されました。過去11年間で、彼らはEpic Food Fight、Technolites、Chronique des Silencieux、Sovereign Syndicateなどのタイトルを出荷しました。
2024年11月6日に、彼らはThrive:Heavy Lies the Crownを早期アクセスでリリースし、素晴らしいレビューを受けました。このゲームは、中世の都市建設者で、リアルタイム戦略要素とシングルプレイヤーおよび協力的マルチプレイヤー機能の両方を備えています。プレイヤーは戦略的に自分の領土と王国を拡大し、大きなマップに沿って建設を進めることができます。彼らの旅が進むにつれて、王国の運命は彼らが下すすべての決定にかかっています。
今日、チームはゲームの1.0バージョンを発表しました。私たちはZugalu EntertainmentのCTOであるGarrett Hauと、チームのリードコンセプトアーティスト兼テクニカルアーティストであるJackie Liと座談し、彼らが直面したパフォーマンスの課題と、カスタム植生システムの構築がゲームの最適化においてどれほど重要であったかを話し合いました。
カスタム植生システムの構築
ゲームは荒野を舞台にしているため、多くの木、草、茂みなどがあります。元の実装では、草が非常にまばらでしたが、チームは広大で豊かな草原を作りたいと考えていました。「草原バイオームなど、さまざまなバイオームのために、地面を植生で覆いたいと思っていました」とHauは言います。それを実現するためには、高い数の植生インスタンスを処理できるシステムが必要でした。
プロジェクトの大部分の間、チームはサードパーティのソリューションを使用しており、うまく機能していましたが、CPUに大きく依存しており、約3ミリ秒のコストがかかり、ゲームは非常にCPUボトルネックになっていました。彼らはかなり低いシステム要件を持っていたため、ほとんどの計算をGPUに移行し、異なる種類のソリューションが必要だと判断しました。
「私たちは独自のシステムを作ることに決め、同時にゲームのタイルベースの性質と統合することにしました。元のシステムには独自の動作方法があり、タイルごとのレベルでの特定の相互作用はコストが高すぎました」とHauは言います。

彼らはそれを修正し、より高密度の植生を持つことができるようにしたかった。新しいシステムではGPUで計算していたため、パフォーマンスの余裕があり、繁栄する森林を作る機会があった。
もう一つの重要な目標は、タイルごとのマスキングを取得することだった。「以前は、道路を配置すると、効率的にマスクできなかったので、植生が道路の上に成長してしまった」とハウは言う。初期の方法はCPUに依存していたため、追加のマスクがそれに負担をかけ、彼らは道路や何でも本当に、草や植生をパフォーマンスを消費せずにマスクしたいと考えていた。

コンピュートシェーダーで実行中
チームは、大きなマップ全体に植生をスポーンさせる際に大きなボトルネックを経験した。ゲームには豊富な植生があり、マップを迅速に移動できる非常に高いカメラがあるため、植生がプレイヤーを止めることなくスポーンすることが重要だった。これは非常に難しいことが判明した。
「植生を見ると、数十万のインスタンスをスポーンさせる必要があることがわかります。だから、私たちはこの目的のために設計されたGPUインスタンシングを選び、数百万のインスタンスを可能にしました」とリーは言う。
まず、チームはGPUインスタンシングのためのデータを準備した。彼らは、植生をスポーンさせたいすべての位置の配列を構築し、GPUに供給する必要があった。植生はタイルマスキング以外でCPU側と実際に相互作用しないため、彼らはこれをコンピュートシェーダーで実行した。コンピュートシェーダーがレンダリングシェーダーが実行される前にGPUで実行されたため、彼らはコンピュートシェーダーでデータを準備し、インスタンシングのために結果のデータを供給した。これはインスタンス間接とも呼ばれています。

次のステップは、コンピュートシェーダーの使い方を理解することで、比較的簡単であることが証明されました。Liは、「コンピュートシェーダーはGPU上のマルチスレッド操作に過ぎません。」と説明しています。私たちのケースでは、各インスタンスデータは各スレッドで個別に計算できます。それはUnityジョブシステムのようなものですが、GPU上でのことです。」
マルチスレッド環境で作業する際は、各スレッドの作業負荷を調整して、他のスレッドの実行に依存しないようにして、パフォーマンスを最大化する必要があります。
Liは、「例えば、ランダム性を追加する際には、パーリンノイズ、シンプルノイズ、またはハッシュ関数のようなものを使用します。」と言います。現在評価されている表面は均一なグリッドに分割されており、各スレッドは各グリッドポイント内で操作するため、重複した植生が重ならないようにすることができます。
地形はランタイムで変形できなかったため、開発の開始時にこのデータを取得し、GPUに渡しました。これにより、高さデータの前処理が可能になり、特に各高さ位置での傾斜を計算するために、植生が地形の輪郭に従うようにカスタマイズできました。

コンピュートシェーダーの統合
チームはコンピュートシェーダーを使用していましたが、望むデータを取得するために多くのシェーダーを実行する必要がありました。描画呼び出しと同様に、少ない方が良いです。彼らは、ディスパッチ呼び出しの半分を排除し、CPUからGPUへのデータ転送を1つのAPI呼び出しに統合することで、GPUコマンドの数を減らそうとしていました。
「私たちの植生システムは、多くの異なるタイプの植生で構成されており、各タイプの植生にはコンピュートディスパッチが必要です。」とLiは説明します。「50の植生がある場合、それは50のディスパッチであり、それぞれにn個のスレッドがあります。」
各スレッドの目標は、インスタンス位置と他のデータを計算することでしたが、スレッドがマスクされた位置またはカメラのフラスタムの外にあるカリングされた位置を計算する可能性も高く、その場合、データは後で植生を描画するために使用されるインスタンス配列に追加されません。

スレッドが配列に追加するかどうかは不明なため、スレッドセーフなリストのようなデータ構造を使用し、有効な値をリストに追加しました。HLSLは、アペンドバッファの形でこの機能を便利に提供します。「アペンドバッファを使用することには小さな欠点があります」とLiは言います。「追加されたアイテムのカウントを取得するために追加のGPUコマンドを実行し、そのカウントを消去してアペンドバッファを再利用できるようにする必要がありました。」
しかし、コンピュートシェーダーはグループ共有という便利な変数を提供し、スレッド間の通信を可能にしました。そして、それはインターロックド関数と組み合わせることで、各ディスパッチがグローバルインデックスカウンターを追跡できるようにし、有効なインスタンスデータを密にパックし、間接描画コマンドを同じディスパッチ呼び出し内で更新できるようにしました。
CPUデータを送信する際、チームは最初にパフォーマンスのペナルティに直面しました。彼らは、フレームごとに変わる異なる植生タイプのシェーダープロパティを更新する必要がありました。
「最初は、データを別々に送信していたため、50のSetData()コマンドが発生しました」とLiは言います。「しかし、基になるデータ型が同じであったため、すべてのデータを1つのバッファに統合し、各植生タイプにそのバッファへのオフセットインデックスを提供しました。これにより、SetData()コマンドは1回だけで済みました。」
チームは保守的に、CPU時間を0.1ミリ秒節約したと見積もっており、これは合計0.5ミリ秒の20%に相当します。

CPUからGPUへのタイルデータの送信
マップが非常に大きく、約1000万タイルであったため、チームはCPUからGPUへのタイルデータを取得するのに苦労しました。「フレームごとに数百万のタイルをGPUに送信しようとするのは非常にコストがかかるでしょう。なぜなら、送信するデータが非常に多いからです。画面を占有するのに十分なデータのサブセットを送信できる必要がありました」とHauは言います。
彼らはそれを達成するためにUnityのジョブシステムを使用しました。それはCPU側に役立ち、データを取得してGPUに送信するためのマルチスレッド方式を提供しました。Hauは、「配列からデータを取得することに関しては、ジョブシステムによって加速できる完璧なワークロードです。」と説明しています。
各スレッドはデータのセグメントを取得するために実行され、その後、宛先の配列にコピーされます。同時に、彼らは元の16ビットデータをコンピュートシェーダーで使用されるパックされた32ビットデータに変換しました。
チームは、ジョブシステムデータにバーストコンパイラーを適用して最適化されたコードを作成しました。「バーストコンパイラーはマルチスレッド性能を大幅に向上させました。属性を追加すると、すぐに1ミリ秒を超える時間から0.3ミリ秒未満に短縮されました。1行のコードを追加するだけで非常に印象的でした」とHauは言います。

チームは大きな最適化の成果を経験しましたが、すべての植生をレンダリングする性能についても注意が必要です。
「性能の節約に喜んでいますが、オーバードローは私のシステムが解決できない問題です」とLiは説明します。「それを念頭に置く必要があります。それでも、ゲームの出来には非常に満足しています。」
Unityで作られたプロジェクトについてもっと読むには、リソースページを訪れてください。
