Skip to content

Audio: Pin UI sounds in memory and preload UISndNewIncomingIMSession to prevent frame hitching - #6264

Open
Shadowolf7 wants to merge 2 commits into
secondlife:developfrom
Shadowolf7:ll-fix/notification-sound-hitch
Open

Shadowolf7 wants to merge 2 commits into
secondlife:developfrom
Shadowolf7:ll-fix/notification-sound-hitch

Conversation

@Shadowolf7

Copy link
Copy Markdown
Contributor

Description

This PR fixes noticeable frame hitches/freezes that occur when receiving notifications (especially incoming direct messages from objects or users with sound).

Root Causes

  1. Un-preloaded notification sound: UISndNewIncomingIMSession (the notification sound played when a new IM session begins, such as from scripted objects or users) was never included in init_audio()'s preloaded sound list. When a notification arrived, the viewer initiated an on-demand decode and disk write.
  2. Aggressive 30s buffer purge: In LLAudioEngine::idle(), any audio buffer not actively playing with mLastUseTimer > 30.0f was unconditionally flushed and deleted from memory (mAudioDatap->mBufferp = NULL). Even sounds that were preloaded at startup were wiped within 30 seconds of quiet time.
  3. Synchronous main-thread disk I/O: When LLAudioSource::play() was invoked for a notification sound whose buffer had been evicted (or never loaded), LLAudioData::load() called mBufferp->loadWAV() synchronously on the main thread, blocking the frame while opening, reading, and parsing the WAV file from disk.
  4. LRU buffer reclamation: In LLAudioEngine::getFreeBuffer(), buffers could be reclaimed if all were allocated, evicting preloaded UI sounds.

Solution

  • Pin UI Audio Buffers: Added mPinned flag to LLAudioData and LLAudioBuffer (with setPinned() / isPinned()).
  • Exempt from Eviction: Exempted pinned buffers from the 30-second stale purge in LLAudioEngine::idle() and from LRU reclamation in LLAudioEngine::getFreeBuffer().
  • Dynamic Pinning on UI Play: In LLAudioSource::play(), if mType == LLAudioEngine::AUDIO_TYPE_UI, dynamically pin the audio data and buffer.
  • Immediate In-Memory Buffer Loading: In LLAudioEngine::preloadSound(uuid, pin_buffer = true):
    • If decoded audio data already exists on disk, immediately load it into mBufferp and pin it.
  • In tryFinishAudio() (llaudiodecodemgr.cpp): If the decoded audio asset is pinned, immediately load it into memory on decode completion so future plays are zero-latency.
  • Complete Preload Coverage: In init_audio() (llvieweraudio.cpp), added UISndNewIncomingIMSession and UISndChatPing to the preloaded sound list with pin_buffer = true.
  • Buffer Headroom: Increased LL_MAX_AUDIO_BUFFERS from 40 to 80 (providing headroom for pinned UI sounds alongside the 30 audio channels).

Related Issues

  • Relates to UI frame hitching on notification/dialog audio playback.

Checklist

  • I have provided a clear title and detailed description for this pull request.
  • I have tested the changes locally and verified they work as intended.
  • Code follows the project's style guidelines.
  • I have reviewed the contributing guidelines.

Additional Notes

Tested locally with incoming object IMs, notification alerts, and repeated sound playback. Frame times remain smooth when notifications arrive without any disk-read hitching.

…to prevent frame hitching

- In LLAudioEngine, audio buffers were subject to aggressive purging in idle() after 30 seconds of inactivity, and to LRU reclamation in getFreeBuffer().
- Preloaded UI sounds that hadn't fired recently had their decoded audio buffers deleted from memory. When a notification arrived (e.g. object IM or chat alert), LLAudioSource::play() was forced to synchronously read and parse the WAV file from disk on the main thread via loadWAV(), causing noticeable frame hitches.
- Furthermore, UISndNewIncomingIMSession was never preloaded in init_audio(), meaning incoming object IM sessions always incurred full on-demand asset decode and disk I/O.
- Increase LL_MAX_AUDIO_BUFFERS from 40 to 80 to guarantee headroom for pinned UI sounds alongside the 30 audio channels.
- Add pinned buffer tracking (mPinned) to LLAudioData and LLAudioBuffer to exempt UI sounds from 30s stale purging and LRU reuse.
- Ensure preloadSound() immediately loads decoded WAV data into a buffer if available on disk and marks it pinned.
- In tryFinishAudio(), if an audio asset is pinned, immediately load it into a pinned buffer upon decode completion.
- In LLAudioSource::play(), dynamically pin buffers for AUDIO_TYPE_UI.
- In init_audio(), preload UISndNewIncomingIMSession and UISndChatPing, pinning all UI sounds into memory.
…to preloaded UI sounds

- Change preloadSound() default parameter pin_buffer from true to false to prevent inventory sound previews and decode retries from unintentionally pinning buffers into RAM.
- Remove dynamic UI pinning from LLAudioSource::play(); active playback is already protected from idle eviction and LRU reclamation by mInUse, and fixed UI sounds are preloaded with pin_buffer = true in init_audio().
- Preserve mPinned state on decode reload retry in LLAudioData::load().

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR targets notification-related frame hitches by ensuring key UI sounds are preloaded and kept resident in audio buffers, preventing on-demand synchronous disk I/O and buffer eviction from impacting frame time.

Changes:

  • Add “pinned” state to LLAudioData / LLAudioBuffer and exempt pinned buffers from idle purge and LRU reclamation.
  • Extend preloadSound() to optionally pin and eagerly load an existing decoded sound into an in-memory buffer.
  • Preload and pin additional UI sounds at startup, and increase the max audio buffer pool size to provide headroom.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
indra/newview/llvieweraudio.cpp Pins startup-preloaded UI sounds and adds missing UI notification sounds to the preload list.
indra/llaudio/llaudioengine.h Introduces pin APIs/state and extends preloadSound() signature to support pinning.
indra/llaudio/llaudioengine.cpp Prevents eviction of pinned buffers and adds eager-load + pin behavior to preloadSound() / LLAudioData::load().
indra/llaudio/llaudiodecodemgr.cpp Loads pinned decoded assets into memory immediately on decode completion.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +761 to +765
adp->load();
if (adp->getBuffer())
{
adp->getBuffer()->setPinned(true);
}
Comment on lines +661 to +665
adp->load();
if (adp->getBuffer() && pin_buffer)
{
adp->getBuffer()->setPinned(true);
}
void triggerSound(SoundData& soundData);

bool preloadSound(const LLUUID &id);
bool preloadSound(const LLUUID &id, bool pin_buffer = false);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants