引言:一次由音频引发的周期性卡顿
同屏爆发 50 个爆炸音效且角色处于混响区域时,游戏出现周期性掉帧并伴随爆音——排查 C# 逻辑毫无头绪,最终靠 Profiler 深入原生层才定位到真相:流式解码与 DSP 图计算超载,音频管线无法按时交付数据。这类事故说明一个事实:C# 层的AudioSource.Play()只是冰山一角,音频体验的下限由原生层的管线设计决定。
本文拆解 Unity 音频引擎原生层的四个核心环节:音频线程与缓冲机制、流式解码管线、DSP 图混音与空间化、以及工程层的性能治理。
一、音频线程:实时系统的硬时限
1.1 为什么必须独立线程
声卡按固定节拍消费数据:48kHz 采样率、1024 样本的缓冲区意味着每约 21.3ms 就必须交付一批新样本。这是硬实时约束——迟到一个样本就会产生爆音(Glitch)。主线程受 GC、渲染、脚本逻辑影响无法满足这种确定性,因此引擎用独立的音频线程驱动混音输出。
两个线程之间的协作依赖两个机制:
- 无锁数据交换:主线程对音频参数的修改(音量、位置)通过无锁队列或原子变量传递给音频线程,音频线程永远不等待主线程的锁;
- 双缓冲/环形缓冲:写入与读取使用不同缓冲区块,硬件中断到来时总有完整的一块数据可交付。
这也解释了音频问题的排查特征:音频线程的开销不会直接体现在主线程调用栈里,需要在 Profiler 中专门观察音频线程的占用。
1.2 音频帧与渲染帧的解耦
音频线程按缓冲大小切分"音频帧"(约 2