版本
menu_open
Wwise SDK 2023.1.3
关于降低内存占用的技巧

依据规定限值削减内存用量并不是一件容易的事。以下是帮助降低内存占用的几个技巧:

Object 内存

Object 内存用量直接受内存中加载的声音和 Event 数量以及游戏对象数量影响。它包含工程中用于实现声音设计的行为所必需的对象的所有属性。它还包含所有游戏对象及其相关信息(游戏同步器值、位置、朝向等)。加载的 SoundBank 越多,所需分配的内存就越多。所需的大小完全取决于在一个场景、关卡、地图、游戏区域等中可能播放的声音数量。您可以采用以下做法来减少这些内存分配:

  • 将装有大量声音结构和事件的大型 SoundBank 分割成小型 SoundBank。作为参考,您可以转到 Advanced Profiler(高级性能分析器)的 SoundBanks(音频包)选项卡,并在 Object Memory(对象内存)列中查看每个 SoundBank 占用了多少内存。然后您可以根据需要动态地加载和卸载 SoundBank。要做策略性地来考虑如何划分 SoundBank。例如,在划分对白 SoundBank 时,要避免创建的 SoundBank 中只包含属于单个角色的对白。相反,应根据环境将对白分组。
  • 使用 AK::SoundEngine::ExecuteActionOnEvent API 来减少事件的数量。Play/Stop(播放/停止)这对事件可以替换成一个播放事件外加调用一次 ExecuteActionOnEvent API 来执行 Stop 动作;Pause/Resume(暂停/续播)也可以用和 Play 事件相同的手法来处理。
  • 严格地管理 Game Object。它们的任务一旦完成,则应立即注销它们。避免保留大量未使用的游戏对象而不注销;这样做毫无益处,只会消耗内存。例如,假想有一个 NPC(非玩家角色)死了,则应注销它的 Game Object;不要将它复用于其他角色。需要时再注册一个全新的角色。一般来说,如果您有数千个游戏对象在活动,则这样的数量实在是太多了。
  • 不要只使用 Actor-Mixer 来组织声音。文件夹和 Work Unit(工作单元)不会占用内存,但 Actor-Mixer(角色混音器)会。除非您想让多个对象共享相似的属性值,且这些值不是默认的属性值,否则仅仅为了编组对象而使用 Actor-Mixer 将浪费内存。当然,这还取决于是否有事件引用 Actor-Mixer(例如通过 SetVolume或 SetPitch 动作)。
  • 尽量降低大型层级结构的大小和复杂性。大型层级结构的常见例子有表现“撞击”或“脚步”的层级结构。由于有大量的变量,因此层级结构可能会很大,并占用大量的内存来容纳结构。以下是减少此类层级结构的几种方法:
    • 如果采用 Switch 切换造成的唯一变化是一个简单属性(样本素材相同,只是音量/音高/随机化器等不同),则应换用 RTPC。
    • 将 Switch Container 层级结构分到多个 SoundBank中去。在 SoundBank Manager 中,当您包含某个 Switch Container 时,还会包含它的所有子分支。然而,您可以在 SoundBank Editor 视图中的 Game Sync 选项卡或 Edit 选项卡中手动排除某些分支。例如,在“脚步”层级结构中,第一个 Switch 变量可以是 Surface Type(地表类型)。然后您可以把多个 Switch 分散放在不同的 SoundBank 中,并根据环境加载这些 SoundBank。您可以创建一个主要的“脚步” SoundBank,让它包含在整个游戏中将遇到的地表类型(城市环境中的混凝土和金属台阶),再创建其它环境相关的 SoundBank 用来装特定的地表类型,例如仅在游戏某一个场景/部分中使用的泥土地。
  • 对于不太需要用 Wwise 的 Actor-Mixer 层级结构来控制的声音,可以使用“External Source”来减少声音的开销。这通常适用于语音素材。

Processing 内存

Processing 类别中的内存用于播放声音。它包含用于解压缩、应用效果器和对音频源做混音的缓冲区。它直接受到同时播放的声音数量的影响。它还受到同时使用的效果器数量和类型的影响。为了节省内存,您需要考虑想同时听到多少声音。某些游戏中很少有超过10种声音的场景,而别的游戏的场景中则可能有数百个声音。您需要考虑最坏的情况。

作为参照指南,我们对一些游戏执行了性能分析(在Xbox One 上),并获得了以下数据:

  • 1 MB 可让您播放大约 42 个声部
  • 2 MB可让您播放大约 96 个声部 虽然这个性能指标几乎是按线性来伸缩的,但真正还取决于使用的编解码器、效果器数量和其他一些因素。例如,使用 Vorbis 编解码器时,根据质量设置,各个声部将多占用大约 50% 的内存。假想同时播放 170 个声音:这样做可能很难让人听清楚各个声音,因此是无用的。然而,需要实验一下才能为您的游戏找到理想数量的声部。使用性能分析器的 Memory 选项卡,对游戏中的多个场景进行性能分析,并记下资源中使用了多少内存。

为了降低处理所需的内存用量,您需要减少同时播放的声部数。通过以下方式可以做到这一点:

  • Playback Limits(Advanced Settings),高级设置中的播放限制。例如,您真的需要听到 50 种子弹飞啸声吗?如果不是,则可将这些声音的个数限制在例如 15 个以内。
    备注: 您还可以对总线也设置限制。
  • Priority(Advanced Settings),高级设置中的优先级。例如,子弹可能没有对白重要。这意味着如果声音太多,首先被踢出的应该是子弹。将优先级与播放限制结合使用。
  • 基于距离的优先级偏置 (Advanced Settings)。距离远的对象通常没有距离近的对象重要。再以子弹为例,如果有 15 个子弹声音距离小于 10 米,则我们不需要听到 10 米以外的子弹声。
  • Below Threshold Behavior(高级设置)。性能最优的选择(针对 CPU 和内存而言)是“Kill Voice”,这对非循环的声音非常有用。性能上的次优选择是“Send To Virtual”“Play from beginning”,再次是“Send To Virtual”“Resume”,最后是“Send To Virtual”“Play from elapsed time”。“Continue to play”和“Play from elapsed time”是性能上最昂贵的选择,Wwise 的默认值是“Continue To Play”。
  • Volume Threshold(Project Settings),工程设置中的音量阈值。这有助于终结声音太小而听不清的声音。此选项结合 Below Threshold Behavior 和 Attenuation 设置(离得越远通常意味着越听不清)一起使用。
    备注: 您可以在运行时以编程的方式更改音量阈值。您可以在游戏中 CPU 运算更繁重的位置使用此设置,以将更多的声部发送到“低于音量阈值”的状态。
  • 更改已经使用的编解码设置(Conversion Settings)。Vorbis 需要额外的内存来解压缩音频。不同的参数可以提高或降低内存需求量。不过要认真权衡:使用其他编解码器或较小的压缩比可以减轻 Processing 内存负荷;但若以 SoundBank 的形式加载,则需在内存中存储较大的文件。在有些情况下,最好在 Processing 内存用量中多分配 500 KB。这样可以节省数 MB 的 Media 内存,从而降低总体音频预算。
  • 更小或更低的品质。某些效果器运算需要大量的内存。一个常见的内存消耗大户是混响效果器,无论采用什么类型的混响,都会消耗大量的内存。实际上,您的游戏不应有很多并发运行的混响。我们一般建议少于 4 个。另外降低混响的品质或长度也会有用。

媒体内存( SoundBank )

SoundBank 占用的内存量几乎都取决于 SoundBank 中的声音数据。控制媒体所占用的内存量可通过以下方式来做到:

  • 将装有大量声音结构和事件的大型 SoundBank 分割成小一些的 SoundBank。根据需要动态加载和卸载。
  • 让更多声音采用从磁盘中流播放(通过声音属性来指定)。对延迟敏感的声音可使用可采用预读媒体来预先加载,或者使用 PinEventInStreamCache API 来按需流到缓存中去。
  • 使用 PrepareEvent() API。
  • 提高对音频的压缩(通过 Conversion Settings、编解码器等)。
  • 使用更低的采样率。另请查看 Conversion Settings 中的 Automatic Sample Rate(自动采样率检测)功能。
  • 将风声类型的声音替换成 SoundSeed Wind/Woosh 插件产生的等效声音。风声往往有较长的循环,可能会占用大量的媒体空间。锋刃的嗖嗖声、螺旋桨声、风通过打开的车窗刮起的呼呼声、通风噪声等都可使用此插件建模。另外也可以考虑非风声应用:可为任何嘈杂的声音建模。示例: 海浪的声音和远处高速公路上传来的声音。

Tuning "Temp Alloc" Memory

Wwise uses an internal pool of memory to manage some temporary allocations that persist for less than one audio-render tick, which are represented in the Advanced Profiler's Memory tab as "Temp Alloc". These temporary allocations exist for a specific amount of time, have very little overhead, are handled internally by the sound engine, and cannot be forwarded to developer-provided memory allocation hooks. Instead, the only allocations in this regard that are observed by the Advanced Profiler and memory allocation hooks are the larger memory blocks that the temporary allocations are made from. Therefore, it might be desirable to manually tune the management of "Temp Alloc" memory blocks, in order to better optimize memory usage in your game.

During AK::MemoryMgr::Init, AkMemSettings::tempAllocSettings controls the behavior of the memory blocks for each Temp Alloc category. Notably, this includes configuring the size of the memory blocks, the minimum number of blocks that the system keeps allocated at all times, and how many blocks have to be unused for a tick before the system starts freeing memory. You can use AK::GetTempAllocStats() to see how much memory the Temp Alloc system uses in your game at runtime, and better fine tune configuration of the system.

The following are some suggestions depending on your game's requirements, or other behavior observed during profiling:

  • If you are not concerned about the overhead of allocating and freeing the memory blocks, or the memory fragmentation that is incurred when the memory blocks are freed, it might be desirable to set AK::TempAllocInitSettings::uMinimumBlockSize to a lower value than the default, so that it better matches the memory usage of your game's needs at any given time.
  • If the allocation and freeing of blocks causes excessive memory fragmentation, you can measure AK::TempAllocStats::uPeakMemUsed to view the Temp Alloc system's peak memory usage, and then ensure that the AK::TempAllocInitSettings::uMinimumBlockCount is set to a high enough value so that all of the blocks you might use are allocated when the sound engine is initialized, and never freed afterward.
  • If you want to avoid freeing memory blocks without evaluating your peak memory usage, you can set AK::TempAllocInitSettings::uMaximumUnusedBlocks to a high value to ensure that the system can allocate new blocks, but not free them, even during periods of low memory load.
  • If you are using the Job Manager for audio rendering, as described in 利用 Job Manager 并行执行音频渲染作业, the number of memory blocks will increase. This is because the memory blocks are all thread-local, and one memory block is typically allocated by each active worker. It might be desirable to lower AK::TempAllocInitSettings::uMinimumBlockSize so that using more workers does not cause a significant increase in used memory in your game.
  • If your memory allocation system includes some metadata adjacent to large memory allocations, it might be worth ensuring that the amount of memory allocated by the memory blocks does not cause a significant amount of waste. Given a requested allocation of 2048 KiB, some memory allocation systems might actually map 2112 KiB of memory. However, a requested allocation might of 2047 KiB would map 2048 KiB of memory. The block sizes do not need to be powers of two, or exactly matched to page sizes, so it might be preferable to set AK::TempAllocInitSettings::uMinimumBlockSize to a slightly reduced value than intended in order to mitigate waste in this regard.

Some debugging options are available in AK::TempAllocInitSettings. In the Debug and Profile configuration of the sound engine, AK::TempAllocInitSettings::bDebugDetailedStats and AK::TempAllocInitSettings::bDebugEnableSentinels are enabled by default in order to improve tracking of usage statistics, and to provide some easy detection of buffer overruns. Disable these options when the highest performance, or most accurate profiling data, is required for your application. Support for these options is removed entirely in the Release configuration of the sound engine.

Tuning "Span Count"

If your Memory Manager integration relies on Wwise's integration of rpmalloc, it might be desirable to adjust AK::MemSettings::uVMSpanCount and AK::MemSettings::uDeviceSpanCount in order to reduce the amount of memory reserved by the system. There are three options available for these settings, which provide different trade-offs for CPU and Memory use: AkSpanCount_Small, AkSpanCount_Medium, and AkSpanCount_Huge.

AkSpanCount_Huge is the default value, which offers the best CPU performance by reducing the number of calls made to AkMemSettings::pfAllocVM, but also because in supported integrations, and on some platforms, memory mappings can be made using 2MiB pages, instead of 4KiB or 16KiB pages. Utilization of 2MiB pages can reduce the number of translation lookaside buffer (TLB) misses during sound engine execution, and help improve overall CPU performance.

AkSpanCount_Small adjusts the amount of memory requested at any given time to be as low as 64KiB. This can reduce the amount of memory reserved by Wwise, but can increase the amount of CPU usage due to an increase in calls to AkMemSettings::pfAllocVM. Depending on your implementation of the AkMemSettings::pfAllocVM callback, this might also prevent the usage of 2MiB pages for memory mappings.

AkSpanCount_Medium balances memory and CPU usage, by requesting memory blocks as low as 512KiB. This can reduce the number of calls to AkMemSettings::pfAllocVM compared to AkSpanCount_Small, but still might prevent the usage of 2MiB pages for memory mappings.

备注: If your implementation of AkMemSettings::pfAllocVM provides blocks of pre-mapped memory, and rarely invokes a system call for a new memory mapping, we strongly recommend that you use a setting of AkSpanCount_Small because the relative cost of calls to AkMemSettings::pfAllocVM should be greatly reduced, and your pre-mapped memory might already be using 2MiB pages.

此页面对您是否有帮助?

需要技术支持?

仍有疑问?或者问题?需要更多信息?欢迎联系我们,我们可以提供帮助!

查看我们的“技术支持”页面

介绍一下自己的项目。我们会竭力为您提供帮助。

来注册自己的项目,我们帮您快速入门,不带任何附加条件!

开始 Wwise 之旅