音频流缓存概述

概述音频引擎使用的流缓存系统

Choose your operating system:

Windows

macOS

Linux

音频流缓存 大大改变了音频加载以及从内存释放的方式。

若在烘焙时启用此功能,几乎所有压缩音频数据都会从 USoundWave 资源分离出来,置于 .pak 文件末尾。这样,音频就能随时加载到内存中,若近期未被使用也可重新释放。

这种内存管理方法在开放世界场景游戏中很流行,在这些游戏中,很难提前确定特定用例的实际音频需求。此法的主要缺点是,由于 USoundWave 需要加载,因此不能保证音频即时播放。但优点是一旦采用这种系统,设计人员就能够在不超出内存界限的情况下引用尽可能多的音频资源。工程师还可利用此系统加载和引用压缩音频数据块,无需依赖音频引擎管理的状态。此外,比起减少不使用流缓存时出现的内存问题,还是减少使用流缓存播放音频时的延迟更简单些。

启用Windows的音频流缓存

目前,唯一支持音频流缓存的桌面平台是Windows。包括Android、iOS、Xbox One、PS4和Switch在内的其他平台也支持。

  1. 打开项目后,从主菜单选择 编辑(Edit)> 项目设置(Project Settings)

  2. 项目(Project) 菜单上,转到 平台(Platforms)> Windows > 音频(Audio)

  3. 启用 使用流缓存(实验性)(Use Stream Caching (Experimental))

TurnOnStreamCaching.png

可在编辑器中为Windows启用流缓存,而无需重启编辑器,但这会停止当前播放的任何音频。

确定正确的缓存大小

除明确标记为留在内存中的标头和音效数据之外, 最大缓存大小(Max Cache Size) 是压缩音频数据使用内存的硬限制。若缓存太小,音效卸载速度过快,会导致加载音效时产生延迟。

极端示例

如果缓存限制设为8MB。每个数据块最大为256KB,所以任何时候缓存里最多有32个数据块。这意味着若一次播放32个音效,就不能再加载更多数据块了。

不那么极端的示例

假设缓存限制设为16MB。缓存里最多可以有64个256KB数据块。若一次性播放32个音效,则还有缓存可再播放32个音效。但若正在播放的32个音效都很长,每个都包含多个数据块,那么按顺序加载的下一个数据块将仍自动加载到缓存。这意味着在此期间,准备要播放的任何音效都将被为32个正在播放的音效而预先加载的32个数据块所堵住。

普通示例

如果将缓存限制设为32MB。它可能包含128个元素,每个元素最大为256KB。如果当前正同时播放32个音效,则还可以在缓存中预存96个额外的音频数据块,避免延迟。若限制为48MB,则可包含192个元素,因此即使正在同时播放32个音效,还可预备160个数据块。

配置缓存大小

  1. 打开项目后,从主菜单选择 编辑(Edit)> 项目设置(Project Settings)

  2. 项目(Project) 菜单中,转到 平台(Platforms)> Windows > 音频(Audio)> 压缩覆盖(Compression Overrides)> 烘焙覆盖(CookOverrides)> 流缓存(Stream Caching)

  3. 设置 最大缓存大小(KB)(Max Cache Size (KB)) 以确定缓存中可容纳的元素数量。

此值若设为 0 ,则此值默认为32KB。最大缓存大小为2,147,483,647 MB,但注意不要设置得太大,以免运行的机器内存不足。

ConfigureCacheSize.png

若在启用流缓存后更改 最大缓存大小 ,需要重启编辑器,使更改生效。

通过及时缓存音效以避免延迟

理想情况下,音效在轮到播放时始终已在内存中。可通过以下方式实现这一点:

准备好要播放的音效

若预测未来将播放某个音效,可在蓝图中调用 Prime Sound For Playback (或C++环境下的 UAudioMixerBlueprintLibrary::PrimeSoundForPlayback ):

PrimingSoundForPlayback.png

例如,在开放世界场景游戏中,当玩家走进汽车周围几英尺范围内时,车辆音效和无线电台就会加载到缓存中。若玩家未进入汽车,音效将保留在缓存中,直至最终释放。

声音提示也可提前准备,但加载完成时不触发委托。( 委托 是一个类,包含指向某对象实例的指针或引用,即要在该对象实例上调用的该对象类的成员方法,以及提供触发该调用的方法。)

PrimingSoundCueForPlayback.png

设置音效的默认加载行为

若要音效在加载后短时间内就播放,可将声波的加载行为设为 加载后准备(Prime on Load)

SetLoadingBehaviorForSound.png

也可将声波的音效类的加载行为设为 加载后准备(Prime on Load)

SetSoundWaveClassLoadingBehaviorForSound.png

此外,还可通过将 au.streamcache.DefaultSoundWaveLoadingBehavior 设为 2 ,将所有声波设为加载后准备。

将音效保留在内存中

若音效在任何情况下都必须无延迟播放,可在USoundWave的整个持续时间内将该音效保留在内存中。

强制声波留在内存中

若将声波的加载行为设为 加载后保留(Retain on Load) ,即是将该音效的第一个音频数据块永久存放在缓存中。

ForceSoundWaveToStayInMemory.png

通过将 au.streamcache.DefaultSoundWaveLoadingBehavior 设为 2 ,可将所有声波设为默认保留第一个数据块。

强制所有声波低于要内联的特定阈值

启用流缓存后,可使用 Stream All Soundwaves Longer Than 设置将所有比特定值短的声波都保留在内存中。若要保证该短声波(例如UI和枪声)首次播放无延迟,且不必检查和测试单独的音效,此设置正当其用。

ForceSoundWavesToBeInlined.png

设置 流送所有超过此值的声波(Stream All Soundwaves Longer Than) 后,低于该时长的音效仍可选择使用声波上的 强制流送(Force Streaming) 复选框进行流送。

ForceSoundWaveLoadingBelowThreshold.png

微调内存

若应用程序需要更多内存,可使用 Trim Audio Cache 函数( UAudioMixerBlueprintLibrary::TrimAudioCache )释放缓存中不使用的音频数据块。

TrimmingMemory.png

Trim Audio Cache 函数迭代缓存并释放未在使用的数据块,直至达到 In Megabytes To Free 参数指定的内存量。此函数返回成功释放的内存。

在C++环境下调用时,此函数是线程安全的。但要记住,它会锁定缓存,开销可能会很大。这意味着此函数运行时,流送音频的缓存可能会欠载。

监控流缓存

可在控制台中键入 stat audiostreaming 以切换缓存显示:

MonitoringStreamCache.png

stat audiostreaming 命令显示缓存中当前已加载的音频数据块,包括最近最频繁使用的数据块到最近最少使用的数据块。

此显示中的数据块采用颜色编码.

颜色

状态

蓝灰色

数据块已加载,但当前未播放。

绿色

数据块当前正在播放。

红色

数据块已被TrimMemory调用删除。

每个数据块都有以下信息:

名称

说明

大小

数据块在内存中的大小,单位KB。

数据块

数据块在 .pak 文件中的索引。

请求计数

此数据块被解码器使用的次数或被准备请求的次数。

使用数据块的解码器数量

同时播放此音效的源的数量。

数据块加载时间

从磁盘加载该数据块所花的时间,单位为毫秒。

名称

此数据块所属的声波的名称。

清理缓存

若尝试在缓存的所有元素都在使用时加载或播放音频数据块(原因是数据块正在播放或正在从磁盘加载),则将清理缓存。在这种情况下,这可确保达到 AudioStreamingCache.cpp

BlowingCache.png

若总是在清理缓存,有五个选择:

  • 增加缓存大小。

  • 减少保留的声波数量。

  • 降低声音限制。

  • 减少清理缓存后尝试加载的音效数量。

  • 忽略它并删除数据块请求。

增加最坏情况下的缓存利用率

在有很多短音效的情况下,缓存中将填充很多小数据块,这样仅用到缓存空间的一小部分。

例如,缓存大小为128个数据块,每个数据块最大为256KB,若加载了大量64KB的音效,就只会用到8M缓存,而上限是32MB。

为了做出补偿,可将数据块最大数量设为大于 MaxCacheSize/MaxChunkSize ,保留池中当前分配的字节数的运行计数器,并在内存计数器达到缓存大小或达到数据块最大数量时,驱逐最早的数据块。使用cvar au.streamcaching.MinimumCacheUsage 确定数据块最大数量。

设置au.streamcaching.MinimumCacheUsage

au.streamcaching.MinimumCacheUsage 可设为 0.0–1.00 。此参数仅可在 IAudioStreamingManager 初始化之前设置。在游戏期间设置此参数不产生任何效果。

增大此值会增加缓存中可存在的数据块的最大数量。例如,若 au.streamcaching.MinimumCacheUsage 为0.75,缓存大小为32MB,则数据块最大数量将为512。若加载了大量64KB音频资源,仍可使用32MB。这意味着 au.streamcaching.MinimumCacheUsage 越接近1,要充分利用缓存所需的数据块平均大小就越小。

au.streamcaching.MinimumCacheUsage

缓存大小(MB)

数据块最大数量

达到100%利用率所需的数据块最小平均大小(KB)

0.0

32

128

256

0.0

16

64

256

0.5

32

256

128

0.5

16

128

128

0.75

32

512

64

0.75

16

256

64

0.825

32

1024

32

0.825

16

512

32

不论单独音频资源有多大,都无法保证100%利用缓存,除非数据块的最大数量为无穷大。

增大 au.streamcaching.MinimumCacheUsage 有如下含义:

  • 由于在最坏情况下缓存中保留的音频更多,所以平均磁盘IO读取的次数会减少。

  • 由于可处理更多较小的数据块,平均内存利用率增加。

  • 查找缓存中数据块所需的平均CPU成本增加。

  • 插入/驱逐数据块的成本保持不变。

使用 au.streamcaching.TrimCacheWhenOverBudget

au.streamcaching.TrimCacheWhenOverBudget 值默认为 1 。这解决了当 au.streamcaching.MinimumCacheUsage 设为任何大于零的值时,流缓存中泄露内存的潜在矢量。为LRU缓存中的大资源而驱逐小资源时,就会发生此泄露。由于这种情况可能会连续多次发生,会导致缓存利用率大大超过目标最大缓存大小。

au.streamcaching.TrimCacheWhenOverBudget 使用的解决方案是去除最近最少使用的数据块,直至回到预算之内。这种权衡在于,准备或播放音效的调用会导致最近最少使用的音效被驱逐。

确定音频读取的优先级

音频数据块是利用 IAsyncReadRequest 的实例从磁盘中读取的,类似于纹理流送和几何体流送系统。因此,可使用cvar au.streamcaching.ReadRequestPriority 将音频数据块加载的优先级置于引擎中其他流送系统之上或之下。此值范围为0–4,0是最高优先级,4是最低优先级。

au.streamcaching.ReadRequestPriority

AsyncIOPriority

其他使用此优先级的流送管理器

0

AIOP_High

动画、纹理、着色器代码

1

AIOP_Normal

异步pak读取

2

AIOP_BelowNormal

几何体流送

3

AIOP_Low

4

AIOP_MIN

缓存音频流送请求的默认值为 2

欢迎帮助改进虚幻引擎文档!请告诉我们该如何更好地为您服务。
填写问卷调查
取消