この記事ではWwise 2022.1のランタイムにおけるCPU使用率の改善点を紹介します:
パフォーマンスの改善
最初にWwise 2022.1全般のパフォーマンス改善についてご紹介します。
RTPC管理の改善
大きなプロジェクトでWwiseを使用する時、多くの開発者たちが経験することがRTPC更新のコストです。プロジェクトが大きくなればなるほどこのコストが極端に大きくなり、パフォーマンスを保つためにさまざまなワークアラウンドが必要となります。例えば読み込むRTPC数を制限するためにサウンドバンク構造を再編したり、Wwiseで作成するゲームオブジェクトを非常に細かく分けて厳選した上で手動で管理したり、反対に対策を全く行わずに別の場所でCPUの使用を削減したりします。
この課題に直球で取り組むことがWwise 2022.1の重要な目標の1つで、負担を大幅に軽減し、パフォーマンスへのコストをこれまでになく予測しやすくすることを目指しました。
これまではWwiseが1つのRTPC更新を処理するたびに、WwiseがアクティブなRTPCサブスクリプションを1つずつ確認し、そのRTPCの更新が必要かどうかを判断した上で、実際にRTPC更新を適用しました。通常は1つのGO(ゲームオブジェクト)に複数のサブスクリプションが登録されているため、影響が大きいのは、例えばリスナーからの相対距離に基づいてすべてのRTPCを更新するというようなアクションを実行した場合で、システム内のGO数が増加すればするほど、複雑さが2次費用関数的に増加します。
今回この処理の大部分を省略できるようになりました。代わりに対象のRTPCサブスクリプションのリストを事前に策定し、RTPCが更新されるたびに発生していた負荷の高いサーチを飛ばします。RTPC更新にかかる時間が劇的に減り、その分処理速度が予測しやすくなるはずです。
パフォーマンス面のメリットは大きく、この問題を迂回しようとこれまでに多大な努力を費やしてきた開発者たちは、今後はそれをやめてゲームとWwiseのやり取りをシンプルにできると思います。
組み込みローパスフィルタ・ハイパスフィルタ(LPF/HPF)の改善
次に紹介するのはボイスパイプラインの組み込みLPF(ローパスフィルタ)とHPF(ハイパスフィルタ)の大きな改善点です。LPFやHPFをボイスに活用するデザイナーは多く、アテニュエーション(減衰)カーブに取り入れるなどオーディオバスやAUXバスへのミキシングの一部として採用する傾向があり、とても一般的な処理です。どちらの使い方も下図の例に含まれていて、例えばMarkers_TestをMaster Audio Busにミキシングする際にLPFを適用し、初期ボイス処理にLPFやHPFを適用します。
この操作自体は過去にもCPUがかなり「過熱」する場所として知られ、Wwiseだけに注目した開発者はもとより、ゲーム全体で命令サンプリングをした経験のある開発者であれば、パフォーマンスの「ホットスポット」となるのを見た方も多いはずです。
私たちは対策としてオーディオ処理パイプラインで実行するコアのミキシングやフィルタリング操作の大部分を書き直しました。特に開発者が同時に数10個や数100個ものボイスでこれらの機能を使用することや、LPFとHPFを組み合わせて使うことも珍しくないという点に注目しました。最近のCPUデザインも考慮した上で最適化し、高性能のパソコンやコンソールのCPUだけでなく、最新のAppleシリコンやARM Cortexなどのチップでもパフォーマンスの改善が期待できます。
CPUやシナリオによってパフォーマンスの改善レベルがかなり違いますが、社内で実施したあるベンチマークで前世代のコンソールでWwise 2021.1.0とWwise 2022.1.0を比較したところ、エフェクトの合計スループットパフォーマンスは2倍の改善が測定され、現行世代のコンソールではスループットパフォーマンスは5倍の改善が測定されました。
特に前世代の開発をWwise 2021.1以前で行い、これから現世代の開発をWwise 2022.1で行う予定の開発者のみなさまは、この機能に関して最大10倍のスループットパフォーマンス改善が見込まれるということです。
スペーシャルオーディオのパフォーマンス改善
Wwiseのスペーシャルオーディオコンポーネントのパフォーマンスも大幅に改善されました。
回折のレイキャスティング
今回スペーシャルオーディオ用の回折(ディフラクション)エッジをワールド内で発見するために、トレーシングレイ( tracing ray)を用いるようになりました。回折エッジの発見は、これまでシーン内のすべてのジオメトリを対象に詳細な調査を行うことで潜在的な回折エッジを見つけてきましたが、この処理でより多くのジオメトリを簡単に使用できるようになるはずです。各エミッタの回折パフォーマンスのオーバーヘッドは減少し不変となり、発射されたレイ数や利用する回折の次数に比例して増加します。
ロードバランスの調整
CPUのスパイクを抑えるために、スペーシャルオーディオのジョブを複数のサウンドエンジンティックに分散できるようになり、これは AkSpatialAudioInitSettings::uLoadBalancingSpread で設定できます。新しいロードバランスシステムの特徴の1つとしてプライオリティキューがあり、レイテンシに敏感な処理はほかの必要な仕事よりも相対的に早く実行できます。つまり新しいサウンドの再生やアクティブなルームの切り替えは、複数のオーディオティックに負荷を分散させたとしても非常にすばやく行われます。
その他の小さなハイライト
最後にWwise 2022.1に含まれるその他の小さな改善点や、Wwise 2021.1のパッチリリースの一環としてリリースされた特筆すべき改善点を紹介します。
- AK CompressorやAK Expanderエフェクトを書き直し、多くのシナリオでスループットパフォーマンスが最大4倍も改善されました。アンビソニックスの処理などの一部のシナリオではさらに大きく伸び、スループットパフォーマンスが20倍以上も改善されました。
- AK CompressorやExpanderの変更に関係する別のメリットとして、異なるチャンネルコンフィギュレーション間で散見された一部の動作の不一致を解消することもできました。この問題を補うために、特に特定のチャンネル数や3D Audioを使用した時などにミックスで調整してきた場合は、おそらくもうその必要はありません。
- ハードウェアアクセラレーションを有効にしたAK Convolutionエフェクトと、ハードウェアアクセラレーションを有効にしたWEM Opusデコードを完全に非同期で処理できるようになりました。その結果これらの機能に費やすCPU実時間の大部分が不要となり、以前と比べてコストがはるかに小さくなりました。ハードウェアアクセラレーションであるため、当然これらの変更は該当するプラットフォームでしか適用されず、詳細は各プラットフォーム専用ドキュメントで確認できます。
- 最後にサウンドエンジン処理の一部として利用できるWwiseのデバッグ機能 AkInitSettings::bDebugOutOfRangeCheckEnabled についてです。この機能自体は新しいものではなく、誤った値、例えば無限(infinity)や非数(not-a-number)や単に極端に大きいオーディオ値がボイスパイプラインのどこに出現しているのかを追跡しやすくするためのもので、数リリース前からあるツールです。この機能が以前よりもさらに速くなりました。まだ多少はパフォーマンスへの影響がありますが、サウンドエンジンの実行時間を何倍にも増やすものではなく、サウンドエンジンの実行時間が増えるとしても3倍ではなく1.1~1.2倍程度です。問題を洗い出す時のデバッグ作業を効率化してくれるだけでなく、負荷があまりにも小さいため、あなたのゲームタイトルの開発ビルドで常時オンにすることも検討の範囲内と言えます。
CPUプロファイリングの改善
CPUパフォーマンスの改善だけでなく、CPUのプロファイリングも改善し、CPUのパフォーマンスを調査する開発者がより 対策をとりやすい 情報を提供するようになりました。
主な変更点はSDKの以下の新しいコールバック関数の追加です:
- fnProfilerPushTimer
- fnProfilerPopTimer
- fnProfilerPostMarker
fnProfilerPushTimer と fnProfilerPopTimer はWwiseフレーム全体にわたり頻繁に呼び出されるコールバックで、特定のCPUイベントの開始と終了を示すものです。開発者が使用するCPUプロファイラには、Superluminal、RADのTelemetry、UnrealのTiming Insights、プラットフォームの専用プロファイラ、さらにゲームのツールセットの一部として使用するその他の独自ツールなどがありますが、それらに対してより詳細なタイミングデータを提供するために使います。下図はそのようなプロファイラでこれらのタイマーを適用した時の画面です。
fnProfilerPostMarker はほかの独立したイベント、例えばプロファイラでブックマークの追加などを表示するため使用しますが、現時点では“Voice Starvation”と“Audio Frame Boundary”のイベントだけに使用しています。開発者が大きいプロファイラセッションを閲覧しながら、どこでWwiseの“Voice Starvation”が発生したのかを見つけ、プロファイリング作業でフォーカスすべき場所を特定するために便利な機能です。
これらのコールバックを一部のプラットフォーム専用プロファイラで統合した例がWwise SDKにあり、機能内容を分かりやすく示しています。これらは SDK/Samples/SoundEngine にあり、下図がその1例です:
おそらくオーディオプログラマーも 非オーディオプログラマー も、これを利用してWwise内部のどこにCPU時間が費やされているのかを理解して整理できるようになり、サウンドエンジンを変更したり、プログラムカウンターのサンプリングだけからパフォーマンス状況を推測したりする必要はありません。
これらの新しいタイマースコープは外部ツールとの統合のためだけでなく、このデータはWwiseのプロファイリングセッションにも含まれるようになりました。
上記のSDKプロファイリング機能と同じデータが提供されますが、それ以外にも特定のボイスやバスにタイマースコープの追加属性を提供します。
ただしこれは現時点で上級ユーザだけを対象としており、使用するためには隠れた設定をトグルしてWwise Authoringツール内で有効にする必要があることにご注意ください。お試しになりたい場合はお問い合わせください。まだ初期の段階であるため提供中のほかのツールほど仕上がっていなく、データを適切に解釈するためにはWwiseランタイムがCPU上でどのように処理されているのかを深く理解することが絶対的に必要です。とは言え、この情報はCPU Dataを有効にしたプロファイリングセッションに必ず含まれているため、サポートチケットの一部としてプロファイリングセッションを提供する場合は、この情報であなたが遭遇したパフォーマンス問題をよりよく理解することができます。
以上のことで「コストの大きいプラグインやエフェクトはどれ?」といった大まかな感覚で状況を探るのではなく、パフォーマンス問題を具体的なバスやボイスやWwiseの具体的な機能にまでさかのぼることができると考えています。CPUパフォーマンス問題が把握しやすくなり、これまで以上にはっきりとした目的や精度の高い方向性に基づいて対策をとり、CPUパフォーマンスを左右することができるはずです。
CPUスケジューリングの強化
次に私たちはWwiseの課題として控えめですが、影響度の大きいゲームエンジンとのCPUリソースの譲り合いと調整について検討しました。
CPUスケジューリングの問題
Wwiseを開発した当初は、ゲームが主に「System-on-a-thread(システムオンスレッド)」と言える時代でした。ゲームのスレッド数は比較的少なく、まだマルチコアCPUが広く活用されていませんでした。
この時はWwiseがさほど邪魔にならない方式で稼働できました。専用のスレッドがありそれを定期的に起動し、ゲームフレームのほかの部分をあまり邪魔しませんでした。CPUを一杯にするほどのスレッド数がない状態であるか、あるいはスレッドをそれぞれ違うコアで実行して複数のコアで自由にスケジューリングできる状態でした。
ところがその後、多くのゲームがCPUの使用方法を発展させました。再利用可能なワーカースレッドのスレッドプールで、仕事をタスクやジョブとして実行するタスクスケジューラやジョブマネージャの方式がよく採用されました。これでゲーム内の個々のシステムが互いに非同期的かつ同時進行で、仕事を小さなジョブとして実行することができます。こうした仕事の分配パターンはマルチコアCPUを最大限に活用できる、非常にスケーラブルで柔軟で高性能なパターンです。今どきの大規模ゲームの大半がこれに似たかたちで仕事を実行していると言えます。
ただし…Wwiseは未だ自由にスケジュールできる専用のスレッドで実行されることが多く、これが不満の原因となることがあります。
このようなゲームで起こりうる簡単なCPUタイムラインを例に説明します。
メインスレッドはゲームの「シミュレーション」更新のために複数のステージに分割され、“PrePhys anim”や“PostPhys Anim”の仕事を開始する時はその仕事を任意の数のCPUコアに分散させることができます。ゲーム更新をすすめるためには、すべてのジョブを完了させてからでないと前進できません。
ここでWwiseの“EventMgr”スレッドが仕事をしようと入り込み、ジョブ実行中でなかったワーカースレッドのスケジュールが解除された場合は、複数あるスレッドの1つがPostPhysAnimジョブの一部を担当することができなくなるため状況が少し悪化しますが、それでもどうにかなります。すべての仕事が妥当な範囲内の時間に完了し、パフォーマンスへの影響はごくわずかです。
ところが稀に以下のような状況が発生してしまいます:
この例ではEventMgrスレッドがウェイクアップされて1つのPrePhys Animジョブの実行の真っ最中にスケジューリングされました。
その結果ワーカースレッドのスケジューリングが解除され、EventMgrがオーディオティックの実行を完了するまで全く前進できません。ここで最も影響が大きいのは、Animジョブがその完了をゲームスレッドに適切なタイミングで通知できないことで、結果的にMain Threadはこの仕事が完了するのを待機するしかなく、かなりの時間を無駄にしてしまいます。
これは非常に特殊な例で、各処理はそれぞれ同じCPUサイクル数を使いますが、EventMgrスレッドのタイミングが悪かったことで、結果的にMainスレッドが1フレームあたり16ミリ秒というデッドラインを大幅に過ぎてしまいます。最終的にこれが原因でフレームがドロップされることがあり、ゲームのビジュアル表現や全体的なフィーリングに支障をきたします。
この問題はすでに多くの開発者が経験しているもので、以下のようなさまざまな解決策が実行されているのを私たちは見てきました:
- 1番シンプルなソリューションはWwiseだけのコアでWwiseを稼働させることです。ただしゲームが使用できるワーカースレッドが1つ減るというデメリットがあります。ゲームのフレームタイムが安定するかもしれませんが、ゲーム更新やレンダリング更新のピークスループットが最大15%も減少するかもしれません。
- Wwiseを修正してオーディオティックを実行する時にゲームエンジンのワーカースレッドでジョブを起動させようとする開発者もいますが、エンジンを変えると将来的な更新が難しくなることがあり、開発者にとっても膨大な追加作業が発生します。
- さらに極端な場合はEventMgrスレッド(つまりLEngineThread)を無効にしたうえで、手作業でRenderAudioの完全な実行を試みる開発者もいますが、これにはパフォーマンス面でほかの厳しい制限が必要となったり、オーディオのスケジューリングに別の問題が生じたりします。
なお、これは問題を特定するために開発者の時間と労力が確保できていることが前提です。ゲームを出荷し終えただけかもしれませんが、技術的、批評的、また消費者のフィードバックで影響を与えるものの1つとして、ゲームのフレームレートのばらつきや、なんとなくゲームが「粗い気がする」という反応が出てくるかもしれないのです。
CPUスケジューリングの改善
Wwise 2022.1ではこの問題の解決策をサウンドエンジンに組み込み提供しています。その名も「ワーカー関数」コールバックです!
AkInitSettings に新しいstruct“AkJobMgrSettings”があり、開発者が実装して指定することのできる新しいコールバック“fnRequestJobWorker”が含まれます。Wwiseが実施すべき仕事があると、Wwiseがこの新しいコールバックを呼び出すことがあります。
このコールバックの最初のパラメータは、呼び出すことができるWwiseの関数ポインタ in_fnJobWorker です。Wwiseが fnRequestJobWorker を呼び出すと、 in_fnJobWorker が呼び出されるだけです。in_fnJobWorker の呼び出しのタイミングや、どのCPUコアなのかは決まっていません。
ここで注目すべきなのは、下図のように in_fnJobWorker をゲームエンジンジョブ内で呼び出すことができることです:
こうしてWwiseの仕事がゲームエンジンと協調してスケジュールされます。
例えばWwise WorkerのリクエストはPrePhys Anim Jobたちがまだ実行中にスケジューリングされましたが、ゲームエンジンはそれを後で実行するようにスケジュールし、PrePhys Anim Jobの実行中断を回避します。
おかげでWwiseの仕事がゲームの実行を邪魔することが大幅に減り、処理も安定しサウンドエンジンをWwise向けに迂回させる必要もなくなります。
ただこれにも欠点が1つだけあり、それはPostPhys Anim Jobの実行が少しだけ影響を受けてしまうことです。PostPhys Anim Jobもやはりゲームスレッドにとって極めて重要な仕事であるため、その実行がわずかに遅延するだけでもゲームスレッドが少しだけ遅れてしまいます。
ワーカー関数の機能の1つがこの問題の対策として役立ちます。先ほど提供した in_fnJobWorker を呼び出す時のパラメータの1つに、ワーカーを実行する長さを指定できるものがあり、マイクロ秒単位で指定できます。
ワーカー関数の実行中、つまり具体的には各タスクを完了した後に、それがどれだけ長く実行されたのかをワーカー関数が以下のようにチェックします。
提供されたデッドラインをワーカーが超過した時点でWwiseは別のワーカーのための新しいリクエストを発行し、残りの仕事は後で再開できるようにし、ワーカー関数はゲームエンジンに戻ります。
サウンドエンジンティックがすべて完了する前にワーカー関数を終了することが可能となり、実行が少しだけ細分化されます。例えば上記の例では、PostPhys Anim Jobの仕事を少しだけ早めに開始するような使い方が考えられます。
これはやや理想的なシナリオですが、平均的にみると最終的にメリットがあると思います。この値の微調整にゲームのCPUタイムラインを仕上げる段階で時間をかけてみる価値があり、ゲームのジョブやスレッドのさまざまな優先順位や相性のバランスをとることは、最適化された仕事のスケジューリングにつながるはずです。
並列処理と並行処理の改善
協調性のある仕事のスケジューリングを可能にする変更点に加え、最近のCPUの並列処理や並行処理の活用も改善しました。
CPU並列処理の改善
Wwise 2022.1ではマルチコアプロセッサを効率的に活用するための独自タスクグラフスケジューラを社内開発しました(これがAkJobMgrです)。新しいシステムはこれまでマルチコアプロセッシングを行うためにWwiseで使用してきた旧“Parallel-For”機能を完全に置き換えるものです。
Worker Request関数を実行できるようにあなたのシステムをいったん設定してしまえば、さらに広くCPUを利用するために必要な追加手順はさほどありません。基本的には AkJobMgrSettings::uMaxActiveWorkers を1より大きい値に設定し、その動作に AkJobMgrSettings::fnRequestJobWorker コールバックが対応するようにするだけです。
ボイスグラフ処理の改善
下の例は大規模な制作中のタイトルにおいて旧Parallel-For機能がボイスグラフのマルチコア実行を扱う様子を表しています。特に注目していただきたいのはWwiseが幅広く広がる部分で、グラフの各レベルの実行時だけに限定されていて、バス同士の実際のつながりやデータの依存性とは無関係です。
複数のバスが実は互いに独立して稼働できる一方、仕事を配分するシステムではCPUの仕事をそのように表現できないため、実現できる並行処理の程度が制限されてしまいます。
今回、下の図に示すように依存性のあるバスがすべて実行を完了した時点で、Wwiseがすぐに仕事をバスグラフで実行できるようにし、より幅広く実行できるようになりました。
仕事を今までより多くのコアで同時にスケジュールできるようになり、仕事が早く完了します。
さらに複数のバスの実行の一貫性を、これまで以上に確保できます。例えば下図のバスグラフの青色のバスで処理がとても長くかかる場合…
…どちらのシナリオにおいても、下図のオレンジ色バスをすべて青色バスと同時に実行できます。
例えば青色バスがマルチチャンネルの複雑なコンボリューションエフェクトなどを実行するバスで、オレンジ色バスがEQやCompressorの単純なステレオバスである場合など、ほかのバスの実行時間がかなり短い場合は、新しい実行モデルの方がバスグラフ全体の実行にかかる時間がとても短くなります。青色のバスはほかのバスが事前に実行されるまで待つ必要がないため、かなり早くから実行を開始できる可能性があり、実行中に処理できる仕事量が増え、実行のクリティカルパスの長さが大幅に短縮されます。
ジョブディスパッチの改善
AkJobMgrやワーカー関数コールバックのシステム設計で特に気をつけたもう1つのことは、Wwiseがゲームエンジンに返すジョブリクエストの数を、大きく減らすか一定の範囲内に抑えることでした。
私たちは内部ジョブシステムがとてもシンプルになるように心がけ、各ジョブの実行を10~20マイクロ秒で終わらせるようにするなど、パフォーマンスを念頭に注意深く微調整し、ジョブの管理自体のパフォーマンスオーバーヘッドを非常に小さくしています。ただし大型ゲームの大規模なジョブシステムでは私たちの要件よりも機能満載であることが多く、重量級の重さになりかねないことも私たちは理解しています。例えばジョブシステムのオーバーヘッドを最小限に維持するために、ジョブごとの平均実行時間の目安を500~1000マイクロ秒とするジョブシステムも中にはあります。Wwiseのジョブごとに1つのワーカーリクエストを発行してしまうと、多くのゲームエンジンにとって圧倒的な量となり、大々的なパフォーマンス問題が起きる可能性があります。
ワーカーリクエストの数を制御するためにワーカー関数でWwiseのジョブを内部で次々に実行し、新しいワーカーリクエストを発行するのは並行で仕事をする機会がある時だけにし、 AkJobMgrSettings で指定した上限を守るようにします。
このCPUタイムラインの例ではWwiseが内部で取り扱うジョブが23個あり、 AkJobMgrSettings::uMaxActiveWorkers が3に設定されていますが、すべての仕事を完了させるためにWwiseが発行したワーカーリクエストは4個だけでした。“JobMgr::WorkerFunction”ラベルのボックスはそれぞれがワーカー関数のリクエストを表し、その下にあるボックスはWwiseが処理する個々のジョブを表しています。
この場合、Wwiseの実行はサウンドエンジンティックの“SwVoice”(ソフトウェアボイス)フェーズで幅が広くなりますが、“BusTask”(バスタスク)実行は3つのワーカーを同時に実行するには狭すぎるため、そのフェーズのキックオフ時に1つのリクエストしか追加されません。バスのタスクがすべて完了した時点でWwiseは引き続きほかの実行可能なジョブを処理し、サウンドエンジンティックのエピローグにすすみます。
ここで先ほどの例に戻り、WwiseのこれまでのバージョンとWwise 2022.1を比較してみると…
ゲームエンジンがWwiseの仕事をするために1度に最大8個のジョブをスケジュールしようとした場合、前のparallel-forの例ではおそらくゲームエンジンで47個の個別のジョブの処理をスケジュールしようとします。これからはバスグラフを実行するために7個のジョブをスケジュールするだけで済むはずで、これにSwVoiceの処理をちょうど終えたジョブが1個加わります。
まとめ
新しいワーカー関数モデルとマルチコア実行を表現する新たな方式の導入で、新JobManagerが多くの開発者の役に立つであろうと私たちは考えています。スループットの大幅な向上、レイテンシが改善され、ゲームエンジンが仕事をスケジューリングするために必要なオーバーヘッドが大幅に削減されました。開発者は最近のマルチコアCPUの演算リソースをより効率的に使用できるようになり、Wwiseサウンドエンジンの処理性能が上がるだけでなく、ゲーム全体のパフォーマンスが向上すると思われます。
ここでCPUスケジューリングがあまりよくない最初の例に戻り、Wwiseの並列処理を増やした場合を考えてみます。次のような状態を達成しやすくなるかもしれません。
ここではゲームがワーカー関数のコールバックを設定し、コアを独占してしまうのを回避するために早めに終了するように設定し、可能な場合はWwiseをより多くのスレッドで実行できるように設定しています。この例ではWwiseがより早く仕事を完了することができ、ゲームのその他のタイムクリティカルな仕事の邪魔をしません。
オーディオスケジューリングのヒント
上記のまとめの補足として、この新機能を最大限に活かすためのヒントをいくつかご紹介します。システム構成を誤ると状況によっては全体的なパフォーマンスが低下したり、貴重なCPUサイクルが使わずじまいになったりする恐れがあります。
1. 1度に使用できるワーカー数を気軽に使いきらないことを強く推奨します
まずワーカーの起動はコストのない処理ではなく、使う数以上のワーカーリクエストを発行してしまうとCPU時間が無駄になるかもしれません。もし多数のワーカーリクエストがとても早く完了し、サウンドエンジンの処理が常にスケジュールより早めに済むようであれば、Wwiseがリクエストするワーカー数の上限をもう少し低く設定したほうがよいかもしれません。
またサウンドエンジン全体で並列処理を行うわけではないため、8コアにわたり実行したとしてもサウンドエンジンの実行時間が1/8になるとは限りません。
最後に、より多くのワーカーを起動したり、Wwiseのジョブを実行するためにより多くのワーカースレッドを初期化したりすることで、より多くのスレッドローカルヒープやキャッシュが必要となり、より多くのメモリが必要となるかもしれません。短期的に見るとメモリの些細な無駄遣いであったとしても、長期的に見ると例えば、PCで消費者があなたのゲームを128コアCPUで実行している時にゲーム側が無邪気にジョブマネージャを128個のワーカーで稼働するように設定してしまえば、安定性の問題が発生するかもしれません。Wwiseのメモリシステムが割り当てるメモリを制限する設定の場合は、誤ってメモリ上限を超過してしまいオーディオ再生の不具合が生じる可能性があります。
あなたのパフォーマンス要件を満たすために必要なのは最大3~4のワーカーだけかもしれず、それでも大きなメリットがあるかもしれません。もしそれでうまくいくようであれば、そのままで問題ありません。
2. オーディオCPUパフォーマンスはもう気にしなくてもよいと思い込まないようにしましょう
多くの開発者はより高性能なプラットフォームに移行しはじめた時に、よくこう思うのではないでしょうか…
「次世代CPUになったのでパフォーマンスはもう問題じゃないよね?」と。
…ところが数年後には、一杯になった以下のようなCPUタイムラインに直面するかもしれません:
新しいハードウェアであっても油断せず、今はCPU実行の余裕がずいぶんあると思えても注意することが大切です。複数のコアでオーディオを実行できるシステムであっても、一気にCPU時間が無制限となるわけではありません。
実際に現行世代のコンソールのCPUを飽和状態にしているゲームチームも一部見受けられ、ゲームのCPU総使用量に影響する改善点を特定して実施する価値はあり、オーディオパフォーマンスを範囲内に抑えることもまだ検討すべきです。
3. オーディオの実行をタイムクリティカルにする必要がないかもしれません
Wwiseの実行とゲームエンジンの実行のバランスを調整する時に、はたしてオーディオがまだタイムクリティカルであるのか、オーディオ実行を優先すべきなのかを検討してみてください。
これまで私たちが出していたガイドラインでは、EventMgrスレッドの実行を最優先させて必要な仕事を絶対にスケジュールできるように、と推奨してきました。ところが上記の例で見た通り、オーディオ以外のタイムクリティカルな仕事を優先させて単発的に起動させてあげる方が、メリットがあるかもしれません。つまりオーディオ実行のスケジューリングを少し自由にし、「なまける」ことのできるスケジュールにしてこそ、マルチコア処理の威力を発揮できるのかもしれません。
ゲームのタイムクリティカルな仕事を先に終わらせ、ほかのオーディオの仕事の優先順位を「中」や「高」として扱うことで、ゲームのCPUタイムラインでぽっかり空いた時間をオーディオの仕事にあてることができ、かえってよいかもしれません。例えばWwiseを毎フレーム512サンプルの固定設定で実行した場合は、サウンドエンジンが94Hzでしかティックしません。あなたのゲームが目指すビデオレンダーレートが120Hz以上であれば、それが恐らく最重要なパフォーマンス目標であり、使える時間の幅がより狭いため、最初に完了させることの優先度が高くなります。
4. Wwiseの合計レイテンシまたはデバイスバッファサイズを減らします
同様に増えたCPUリソースでオーディオ更新が早く完了することで、Wwiseが使うオーディオバッファ量を減らせるかもしれません。
繰り返しになりますが、Wwiseのデフォルト設定は全プラットフォームで毎フレーム512サンプルであり、システム出力デバイスの初期化は4フレーム分のデータで行います。ソフトウェアレイテンシがバッファデータにして合計2048サンプルとなり、これは42ミリ秒のレイテンシ(@ 48KHz)です。
60または120Hzの動画を目標とするゲームではレイテンシが35ミリ秒未満になる可能性があり、動画よりもオーディオにわずかな遅れが出はじめる可能性があります。クラウドストリーミング環境での実行や3D Audio処理への対応など、ほかにもオーディオレイテンシの懸念点があるかもしれません。
マルチコアリソースを活用することでオーディオを早く完了でき、ほかにも利用できるCPU予算がある場合は、数を減らせないか確認する価値があります。例えばオーディオの実時間が常にスケジュールよりも早い場合は、フレームごとのサンプル数を512から256に減らすことも可能です。反対にオーディオのフレームタイムのスパイクから早く立ち直れるようになり、ボイスのリフィルを削減することも可能かもしれません。
5. 可能であればワーカー間の親和性に配慮してください
次に、もしジョブワーカー同士の親和性を管理することが可能であり、そのような調整ごとを行う時間があれば、実施することを推奨します。Wwiseのオーディオ処理で消費されるCPU時間に大きく影響することがあり、検討に値するでしょう。
一番のおすすめは、システム的に適切であればWwiseワーカーたちを同じCPUクラスタやコアコンプレックスに置くことです。Wwiseはワーカー間やコア間で一部のステートを同期させることが多く、CPUクラスタをまたいでデータを共有する必要があると速度が遅くなることがあります。
もう1点、同時マルチスレッディングに対応したシステムでは、オーディオジョブを1つのコアの2つのハードウェアスレッドのうち、1つだけで行う方がよいかもしれません。オーディオ処理の内容によってはSIMD処理の発行頻度が非常に密となり、CPUコアで利用可能な実行ユニットリソースを飽和状態にしかねません。Wwiseワーカーをできるだけ別々のコアで実行してワーカー同士でこれらのリソースを取り合わないようにした方が望ましいと思われます。特にFPワークロードがはるかに軽いジョブがほかに多くある場合などは、それらをWwiseワーカーと並行してスケジュールできできます。
6. 同時に出されるWwiseワーカーリクエストの数は?
ゲームエンジンによってはゲームのライフタイム中のすべてのシナリオにおいて安定したリソースフットプリントを確保するために、ジョブシステムを事前に作成したり予約したりするメモリやその他のリソースが必要です。これに関連して、1度に何個のワーカーリクエストが出る可能性があり、どれだけのリソースを割り当てるべきかは、Wwiseでリクエストするジョブの内容や頻度が予測不可能であるため、気になるかもしれません。
同じ時に存在するワーカーリクエストの最大数、つまり実行中のワーカーとこれから実行を開始するワーカーリクエストの数は、厳密には「Wwiseがすべてのジョブタイプで使用するために初期設定された最大ワーカー数」ではなく、実はその倍です。
理由として、すべてのワーカーが新しいワーカーを1つリクエストしつつ、もうすぐ終わろうとするワーカーを1つ実行中だという状態が論理的にあり得るからです。
現在と未来
現時点でWwise 2022のSDKにすべての主要なジョブ機能やプロファイラ機能がすでに入っています。入手することであなたもゲームプロジェクトで使用することができ、カスタムプラグインがある場合は動作を確認できます。
Wwise 2022.1のUnrealインテグレーションもこのワーカー関数に対応しているため、その設定をあなたのプロジェクトの初期設定で行えます。
下図はUnrealのTiming Insightsのスクリーンショットですが、WwiseのジョブがUnrealのほかのジョブと協力しながらUnrealのワーカースレッドで実行されているが分かり、詳細なプロファイリングデータもTiming Insights内で直接表示されています。
今後もWwise 2022.1のパッチでさらなるパフォーマンス改善が導入される予定で、その一部について今回の記事でも少し触れました。例えば…
- VoiceやBusの処理だけでなくオーディオティックを完全にワーカー関数で実行するようにして、EventMgrスレッドのフットプリントをほぼ皆無にすること
- 一部のメモリ割り当てシステムで見られるスレッド間の競合を削減するために、エンジンや対応プラグインの一部でWwiseが行う暫定的な割り当ての多くを省くこと
- 複数のジョブにわたる並行処理中にクリティカルな部分を取得する一部のロジックが、全体を大幅に遅くさせることがあるため、それを排除すること。今の時点で特定できているのは、I/Oストリームのリクエストを行うボイスや、GetSourcePlayPositionサポートをリクエストする一連のイベントの中で実行されるボイスなどです。
現在ジョブ化されているのはEventMgrスレッドだけであることを、最後に指摘したいと思います。BankMgrスレッドはまだ更新されていないため、現時点では従来と同様の動作が継続します。しかしこれに関連するパフォーマンス問題も私たちは意識しており、今後のWwiseリリースで解消していきたいと思います。
今回はここまでです
JobManagerシステムに関するほかの情報を確認してみたい場合は、 Wwise SDKドキュメント の“さらに先へ”と“Optimizing CPU usage”の下に記載されています。
リリース後にいただくみなさんのフィードバックは大変参考になります。特にCPUのキャプチャーはこれまでの私たちの決定事項の検証に役立つだけでなく、今後の決定や開発内容にもつながり、大変有益な情報です。
みなさんが経験した問題を共有いただくことで、はじめて私たちもそれを知ることができます。たとえゲームの出荷に問題のフィックスが必要でなかったとしても、問題に遭遇したということを教えていただければ、今後の修正を検討できるため助かります。
ここまでお読みいただきありがとうございます!
コメント