バージョン
menu_open
Wwise SDK 2023.1.3
Optimizing CPU usage

Audio Rendering Thread

デフォルトでは、Wwiseサウンドエンジンは専用のスレッド AK::EventManager で、AkPlatformInitSettings::threadLEngine パラメータが制御しながらすべてのコマンド処理とオーディオレンダリングを行っています。 AK::SoundEngine::RenderAudio をコールするとゲームフレームが終わることを意味するので、スレッドは、 RenderAudio への最後のコール以降に受領したすべてのAPIコマンドを消費することができます。

AkInitSettings::bUseLEngineThreadfalse に設定すると、このスレッドが無効になり RenderAudio が同期的に実行され、コマンドを処理し、必要に応じてオーディオをレンダリングします。オーディオ出力の実際のレートは、オーディオエンドポイントが引き続き制御します。 RenderAudio の呼び出し間隔が、 AkInitSettings::uNumSamplesPerFrame で定義したバッファ期間や、出力サンプルレートよりも短い場合は、 RenderAudio へのコールの一部が、オーディオレンダリングの部分を省略します。逆に、 RenderAudio の呼び出し間隔が出力バッファ期間よりも長い場合は、 RenderAudio が同時に複数のバッファを処理する可能性があり、CPU使用量が急上昇し、次第にオーディオが途切れる可能性があります。

オフラインのレンダリングを有効にすると、非同期コマンド処理や、オーディオスレッドからのオーディオレンダリングが無効になります。 RenderAudio への1回の呼び出しでレンダリングされるオーディオの量は、 AK::SoundEngine::SetOfflineRenderingFrameTime に送る0以外の正の値で決まります。値が0または負の値であれば、強制的に、 RenderAudio で処理されるオーディオバッファがちょうど1となります。

注意: オーディオレンダリングのスレッドを無効にした状態や、オフラインレンダリングを有効にした状態では、同期的な AK::SoundEngine::LoadBankAK::SoundEngine::UnloadBank のAPIコールを、 RenderAudio のコールと同じスレッドで行ってはいけません。理由は、これらのコールが、オーディオバッファがレンダリングされるまでブロックし、それからStop操作を完了させSoundBankメディアを解放する可能性がありますが、これは RenderAudio への並列のコールがなければ起きないからです。

Microsoftのプラットフォームではsingle-threaded apartment (STA) という並列モデルを使用するため、 AkInitSettings::bUseLEngineThreadfalse に設定するときに、 CoInitializeEx() のコールは、 AK::SoundEngine::RenderAudio をコールするのと同じスレッドで行う必要があります。

Query API関数を使う

一部の AK::SoundEngine::Query 関数はCPUのスパイクを引き起こすことがあります。無駄なCPU時間を最小限に抑えてパフォーマンスの最適化を確保するために、以下のガイドラインに従うことを推奨します:

  • Query関数を開発ビルドで使用し、プロダクションビルドで使用しないようにします。
  • Query関数を使用する必要がある場合はSoundEngineグローバルコールバックの中で使用します( AkGlobalCallbackLocation 参照)。例えばRTPC値を読み込む必要がある場合はコードを AkGlobalCallbackLocation_BeginRender または AkGlobalCallbackLocation_EndRender に入れます。

Leveraging the Job Manager for Concurrent Execution of Audio Rendering Jobs

By default, the Wwise Sound Engine executes its various audio rendering tasks, or "jobs", on the audio rendering thread in a sequential fashion. These jobs include, but are not limited to, bus and voice processing tasks.

Concurrent execution of these audio jobs can be enabled by specifying a callback that allows the Wwise Sound Engine to request CPU time on game-managed threads. When the game provides an implementation for this callback via AkJobMgrSettings::fnRequestJobWorker, concurrent execution is enabled in the Wwise Sound Engine.

注釈: When enabling concurrent job execution, some AK::SoundEngine callbacks will be generated from concurrent job worker threads. Additionally, some plug-ins may not be compatible with concurrent job execution.

To understand how the Sound Engine's Job Manager works, it is important to learn the difference between two important callbacks:

  1. The worker request function is defined by the game engine and is called by the Wwise Sound Engine.
  2. The worker function is defined by the Wwise Sound Engine and is called by the game engine.
注釈: The Wwise Sound Engine expects exactly one call to the worker function for each worker request. For example, if the worker request function is called once to request three workers of type AkJobType_AudioProcessing, and then called again to request two workers of type AkJobType_Generic, then the Sound Engine will expect the game to call the worker function three times with AkJobType_AudioProcessing, and two times with AkJobType_Generic. The calls may be issued in any order, sequentially or concurrently from different threads.

When the worker request function is defined, the audio rendering thread will behave as follows:

Figure: Sequence Diagram of Worker Execution
  1. Audio rendering thread identifies jobs that can be performed on multiple threads, such as voice or bus processing.
  2. Audio rendering thread calls the game's worker request function to request CPU time on another thread. In this step, the game engine receives the address of the worker function to call.
  3. The game engine schedules the requests on its thread pool. A number of worker threads are woken up accordingly.
  4. The game's worker threads call the worker functions provided by the Sound Engine in step 2.
  5. Each call to the worker function executes at least one audio rendering job, but can execute more if they are available.
  6. The audio rendering thread resumes processing as soon as all required jobs have finished executing.

This process can repeat multiple times throughout one audio rendering pass, and the audio rendering thread will attempt to pipeline work as much as possible. For example, when rendering the bus graph, processing time for a given bus will be requested as soon as all inputs have finished processing, and may run independently of other busses. In this way, the Job Manager will always attempt to maximize throughput.

注釈: The worker request function can be called from any thread executing Sound Engine code, and must be implemented in a thread-safe manner.

Integrating with a Game Engine's Existing Job Scheduler

The Wwise Sound Engine's Job Manager is designed to work in tandem with existing job schedulers to achieve co-operative multi-tasking. Game engines that already have a job scheduler should implement the worker request function in a way that schedules the execution of the worker function within their existing job system.

When calling the worker function, the game engine's job scheduler can specify a timeout in microseconds. This is to prevent the Sound Engine from taking too much CPU time on the calling thread. Past this timeout, the worker function will stop and request an additional worker if more jobs were available for execution. This allows the execution of other, possibly higher-priority game engine work on this thread.

注釈: Care must be taken when delaying the execution of the worker function or limiting the execution time of Sound Engine work, as this can lead to voice starvation. When integrating Sound Engine jobs into an existing job scheduler, it is recommended to treat audio rendering jobs as high-priority work.

For game engines that don't already have a job scheduler, a sample implementation of such a scheduler is provided in the SDK samples under SDK/samples/SoundEngine/Common/AkJobWorkerMgr.[h,cpp]. This sample provides a great starting point for concurrent execution of audio rendering jobs. Additionally, IntegrationDemo provides code demonstrating how this sample implementation can be integrated in an actual end-user application.

Best Practices When Using the Job Manager

Here are a few recommendations on how best to utilize the Job Manager.

  1. Do not create threads on-demand when new worker requests come in. Creating new threads is an expensive operation on most systems. Instead, pre-allocate a pre-determined number of threads before the Sound Engine is initialized, and distribute worker requests among those threads.
  2. Do not use more worker threads than are necessary to achieve sufficient parallelization of Sound Engine jobs. In some cases, the overhead of requesting workers may result in lost CPU time that would be more effectively used for other tasks. As well, increasing the number of workers may increase the total amount of memory that the Sound Engine requests, due to the use of thread-local caches in memory allocators or other systems in the Sound Engine.
  3. On game-engine worker threads that support execution of jobs, it is recommended to call AK::MemoryMgr::InitForThread and AK::MemoryMgr::TermForThread when initializing and terminating the threads, in order to ensure proper initialization and termination of thread-local memory resources. It is also recommended to call AK::MemoryMgr::TrimForThread when entering a period of inactivity after running the worker function, in order to free up any thread-local memory resources which may not be utilized again in the near future.
  4. On platforms where the number of CPU cores is fixed and known in advance, set worker thread affinities so each thread stays on the same CPU core. Making sure that audio work does not move across CPU cores during execution is desirable to avoid refreshing CPU caches when a thread migrates to a different core.
  5. On systems with multiple Clusters or Core Complexes (CCX), set thread affinities so worker threads and the audio rendering thread all run on the same CCX in order to improve cache coherency across cores.
  6. On systems with Simultaneous Multi-Threading (SMT), set thread affinities so worker threads run on separate physical cores, instead of sharing the same core, to reduce competition on CPU resources.
  7. If you wish to change the maximum number of workers that the sound engine may request without reinitializing the sound engine, you can do so with AK::SoundEngine::SetJobMgrMaxActiveWorkers. This can be useful to dynamically respond to changes in your title's operating conditions, or to more easily experiment and profile different configurations for multi-threaded work.

The recommendations above must be weighed against the other needs of the game. The Job Manager remains a good way to increase overall throughput of the Sound Engine even if some of these recommendations are not followed.

Android

It is not recommended for Android platform to use more than 2 active worker threads for Job Manager, set with AK::SoundEngine::SetJobMgrMaxActiveWorkers. A high number of worker threads on Android devices can lead to higher CPU usage with decreased overall sound engine performance.

Optimizing Job Manager Memory Usage

Running out of memory when allocating a job is considered a critical failure, as the logical flow of audio rendering would be interrupted and unable to resume. This would lead to undefined results and leaks of resources.

To prevent this, the Job Manager allocates slabs of memory that will be kept around and re-used until the Sound Engine is terminated. Most of these slabs are pre-allocated during Sound Engine initialization, but others may be allocated as needed.

You can control the size of the memory slabs, as well as how many are pre-allocated at initialization time, via AkInitSettings::settingsJobManager.

注釈: If AK::MemoryMgr::Malloc fails to allocate a new memory slab during rendering, the Job Manager will keep re-trying the allocation until it succeeds. If you observe the Sound Engine hang during low-memory situations, increase the number of pre-allocated slabs in the initialization settings.

このページはお役に立ちましたか?

サポートは必要ですか?

ご質問や問題、ご不明点はございますか?お気軽にお問い合わせください。

サポートページをご確認ください

あなたのプロジェクトについて教えてください。ご不明な点はありませんか。

プロジェクトを登録していただくことで、ご利用開始のサポートをいたします。

Wwiseからはじめよう