Search Unity

WebGL memory leak related to audio source

Discussion in 'Web' started by Shredsauce, Jun 7, 2019.

  1. Shredsauce

    Shredsauce

    Joined:
    Apr 4, 2016
    Posts:
    37
    Edit:
    It looks like this is a Unity bug. I've tried on the latest 2019 version of Unity and the same thing happens. I also created a brand new project with only one game object in the scene with an audio source attached with "loop" and "play on awake" set. After a few minutes of running, setPosition starts to stack up in ms and the game gradually runs slower.


    I'm getting this crazy memory leak related to the audio sources in WebGL version of my game. It starts off fine running at 60 FPS, even more if you uncap it. But over time the FPS gradually lowers to the point where it's unplayable. As soon as I destroy all of the audio sources in the scene the FPS is back to normal though.

    My temporary fix is to just remove sound in the WebGL version of my game until I figure out how to add audio sources back in. iOS, Android, Standalone Mac/PC all work perfectly. And this happens in every browser on Mac and PC. I'm using Unity 2018.3.8.

    Here's what the call tree looks like in Chrome's developer tools after letting the game run for an hour.
    shredsauce_low_fps_calltree.PNG


    Here's what my audio source setup looks like. There are multiple audio sources and they're referenced in CharacterSounds.cs. I expanded one of the audio sources to show what they look like. Most have loop checked and have an audioclip attached. There is also an audio source somewhere else in the scene for the background wind sound. That one has loop and play on awake checked off. I thought the FPS drop may have had something to do with the multiple audio sources and CharacterSounds.cs script, but even when those are removed the one wind audiosource still causes an FPS drop. It just happens a little slower.

    Screen Shot 2019-06-07 at 9.56.47 AM.png

    And here is what CharacterSounds.cs looks like:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class CharacterSounds : MonoBehaviour {
    4.     private Character character;
    5.     private TurnController turn;
    6.  
    7.     [SerializeField] private AudioSource swishSource;
    8.     [SerializeField] private AudioSource slideSource;
    9.     [SerializeField] private AudioSource windSource;
    10.     [SerializeField] private AudioSource railSource;
    11.     [SerializeField] private AudioSource impactSource;
    12.  
    13.     [SerializeField] private AudioClip snowImpact;
    14.     [SerializeField] private AudioClip railImpact;
    15.     [SerializeField] private AudioClip crashImpact;
    16.  
    17.     [SerializeField] private float maxSlideVolume = 0.32f;
    18.     [SerializeField] private float slideMultiplier = 0.05f;
    19.     [SerializeField] private float swishInterpolant = 3f;
    20.     [SerializeField] private float swishMultiplier = 10f;
    21.     [SerializeField] private float maxWindVolume = 0.05f;
    22.     [SerializeField] private float windMultiplier = 0.1f;
    23.     [SerializeField] private float maxRailVolume = 0.05f;
    24.     [SerializeField] private float railMultiplier = 1f;
    25.     [SerializeField] private float impactMultipler = 0.5f;
    26.  
    27.     private float lastHorizontal;
    28.     private float swishVolume;
    29.     private float slideVolume;
    30.     private float railVolume;
    31.     private float windVolume;
    32.  
    33.     void Awake () {
    34.         character = GetComponent<Character> ();
    35.         turn = GetComponentInChildren<TurnController> ();
    36.  
    37.         if (character == null) {
    38.             Debug.LogError("No Character script attached");
    39.         }
    40.         if (turn == null) {
    41.             Debug.LogError("Could not get TurnController on Character");
    42.         }
    43.     }
    44.  
    45.     void Start () {
    46.         slideSource.Play();
    47.         slideSource.volume = 0f;
    48.  
    49.         swishVolume = 1f;
    50.         slideVolume = 1f;
    51.         swishSource.Play();
    52.         swishSource.volume = 0f;
    53.  
    54.         railSource.Play();
    55.         railSource.volume = 0f;
    56.     }
    57.  
    58.     void OnEnable () {
    59.         turn.OnLanded += OnLanded;
    60.         character.OnEatShit += OnEatShit;
    61.         character.OnRestart += OnRestart;
    62.     }
    63.  
    64.     void OnDisable () {
    65.         turn.OnLanded -= OnLanded;
    66.         character.OnEatShit -= OnEatShit;
    67.         character.OnRestart -= OnRestart;
    68.     }
    69.  
    70.     private void OnLanded (LandingInfo landingInfo) {
    71.         AudioClip audioClip = null;
    72.         if (landingInfo.surface == Ground.Surface.Rail) {
    73.             audioClip = railImpact;
    74.         } else if (landingInfo.surface == Ground.Surface.Box) {
    75.             // TODO should be boxImpact audio clip
    76.             audioClip = railImpact;
    77.         } else {
    78.             audioClip = snowImpact;
    79.         }
    80.  
    81.         impactSource.pitch = Random.Range(0.8f, 1.0f);
    82.         impactSource.PlayOneShot(audioClip, landingInfo.impactForce * impactMultipler);
    83.     }
    84.  
    85.     private void OnRestart () {
    86.         railSource.Play();
    87.     }
    88.  
    89.     private void OnEatShit () {
    90.         impactSource.PlayOneShot(crashImpact, 1f);
    91.         railSource.Stop();
    92.     }
    93.  
    94.     void Update () {
    95.         if (!turn.currentlyColliding) {
    96.             swishSource.volume = 0f;
    97.             slideSource.volume = 0f;
    98.             railSource.volume = 0f;
    99.             return;
    100.         }
    101.  
    102.         float magnitude = Mathf.Abs(turn.rb.velocity.magnitude);
    103.         float deltaHorizontal = 0f;
    104.  
    105.         if (character.horizontal > 0f) {
    106.             if (character.horizontal > lastHorizontal)
    107.                 deltaHorizontal = character.horizontal - lastHorizontal;
    108.         } else {
    109.             if (character.horizontal < lastHorizontal)
    110.                 deltaHorizontal = character.horizontal - lastHorizontal;
    111.         }
    112.  
    113.         if (character.ground.surface == Ground.Surface.Snow) {
    114.             swishVolume = Mathf.Lerp(swishVolume, Mathf.Abs(deltaHorizontal) * swishMultiplier, swishInterpolant * Time.deltaTime);
    115.             slideVolume = Mathf.Clamp(magnitude * slideMultiplier, 0f, maxSlideVolume);
    116.             windVolume = Mathf.Clamp(magnitude * windMultiplier, 0f, maxWindVolume);
    117.  
    118.             swishSource.volume = swishVolume;
    119.             slideSource.volume = slideVolume;
    120.             railSource.volume = 0f;
    121.         } else if (character.ground.surface == Ground.Surface.Rail || character.ground.surface == Ground.Surface.Box) {
    122.             railVolume = Mathf.Clamp(magnitude * railMultiplier, 0f, maxRailVolume);
    123.             railSource.volume = railVolume;
    124.         }
    125.  
    126.         windSource.volume = windVolume;
    127.         lastHorizontal = character.horizontal;
    128.     }
    129. }
     
    Last edited: Jun 10, 2019
  2. Salman_Saleem

    Salman_Saleem

    Joined:
    Aug 30, 2015
    Posts:
    2
    Hi,
    Have you solved this issue ? Or tried any other plugin for AudioSource ?
     
  3. whtsthedeal

    whtsthedeal

    Joined:
    Sep 18, 2011
    Posts:
    4
    Jumping in to hopefully get some insight on this too. I'm seeing this exact same thing across all browsers in my WebGL builds, it seems like Edge handles it the worse (tested in latest 2018 version up most recent 2019 version all with same results).

    Currently I have a test scene setup with only a main camera, directional light, and 7 cubes - each with an audio source that loops and plays on awake (all with the same clip plugged in), there are no scripts at all. Within about 10 minutes of profiling my WebGL build you can see performance start to drop as the AudioSource.Update process time steadily increases. I have seemingly tried every type of audio file type, import setting, audio play/pause/stop (play, playoneshot, playclipatpoint, etc), instantiate/destroy AudioSource, combination to see what may be increasing the process time of the AudioSource calls, but nothing helps.

    I'm honestly surprised this is not a bigger issue as it's a show stopper for any type of development in WebGL that uses the integrated AudioSource.
     
  4. Shredsauce

    Shredsauce

    Joined:
    Apr 4, 2016
    Posts:
    37
    Nope, I still haven't solved this. I'm glad I'm not the only one having issues. Let's hope WebGL audio gets fixed soon.
     
  5. De-Panther

    De-Panther

    Joined:
    Dec 27, 2009
    Posts:
    589
    It might be browser bug in the setPosition method
    https://developer.mozilla.org/en-US/docs/Web/API/PannerNode/setPosition
    but maybe one solution would be to check if the position changed before setting it again.

    You can try to add "soundsetpos.jslib" to Plugins/WebGL folder
    Code (JavaScript):
    1. var LibrarySoundSetPos = {
    2.   JS_Sound_SetPosition: function (channelInstance, x, y, z) {
    3.     if (WEBAudio.audioWebEnabled == 0 || (
    4.         WEBAudio.audioInstances[channelInstance].panner.positionX == x &&
    5.         WEBAudio.audioInstances[channelInstance].panner.positionY == y &&
    6.         WEBAudio.audioInstances[channelInstance].panner.positionZ == z)) {
    7.       return;
    8.     }
    9.     WEBAudio.audioInstances[channelInstance].panner.setPosition(x, y, z);
    10.   }
    11. };
    12. mergeInto(LibraryManager.library, LibrarySoundSetPos);
     
  6. De-Panther

    De-Panther

    Joined:
    Dec 27, 2009
    Posts:
    589
    Ok it didn't help, the position is set according to the pose related to the audio listener, and not related to the world.
    So it did updated the function, but setPosition still get calls in most of the frames.

    Did it happened to you only in Chrome, or also in Firefox?
     
  7. jaltieri

    jaltieri

    Joined:
    Jul 17, 2019
    Posts:
    1
    hello

    this is the response we got from a support ticket:
    followed by:
    lastly, we did not have the issue with Firefox, and are currently using a work-around with an audio-based jslib injection.
     
    Last edited: May 21, 2020
    De-Panther likes this.
  8. BlueStarGeneral

    BlueStarGeneral

    Joined:
    Jan 19, 2018
    Posts:
    4
    I'm having the same issue with 2019.3.12f1. The WebGL version of our game just grinds to halt over time, as the audio clips start playing slower and more distorted over time. Disabling all audio the WebGL version runs fine.

    If it's playing the clips slower then it could be a 3d positioning thing possibly. If it's trying to alter the pitch not sure whats going on with this bug.

    PSD
     
  9. craftx

    craftx

    Joined:
    Oct 2, 2018
    Posts:
    13
    We upgraded Unity to 2019.4.19f1 and still got this same problem.
     
  10. morgansmolder

    morgansmolder

    Joined:
    Jul 20, 2019
    Posts:
    3
  11. Shredsauce

    Shredsauce

    Joined:
    Apr 4, 2016
    Posts:
    37
    Nope, still happens in 2020.3.23f1. I re-enabled sounds in the WebGL version of my game as a test and took this screenshot after letting it run for 30 minutes. My game becomes completely unplayable getting only 1 or 2 FPS. I don't even use SetPosition anywhere in my game.

    Edit: Oops, I misread the Unity version. I will check out 2021.1
     

    Attached Files: