Calling StudioSystem.update() on another thread

We’re on FMOD 2.02.19. Our game occasionally has audio dropouts on level transitions from GC.Collect() blocking too long. FMOD’s RuntimeManager.Update() relies on MonoBehaviour Updates getting called regularly by the main thread but it doesn’t always happen, causing “thread starvation detected” warnings in the log.

It’s my understanding StudioSystem.update() can be called on a separate thread, but FMOD Unity doesn’t do this. Is something like the below snippet our best option? We’ve tried several ways of reducing GC cost.

Is it safe to do this to supplement the call RuntimeManager.Update() already does?

public static class StudioSystemUpdater
{
    public static bool isRunning = false;

    public static void Run()
    {
        float lastUpdateTime = Time.unscaledTime;
        float updatesPerSecond = 20f;
        float secondsBetweenUpdates = 1f / updatesPerSecond;

        isRunning = true;
        while (isRunning)
        {
            if (Time.unscaledTime - lastUpdateTime < secondsBetweenUpdates)
            {
                Thread.Sleep(1); // 1ms
                continue;
            }

            lastUpdateTime = Time.unscaledTime;

            if (RuntimeManager.StudioSystem.isValid())
            {
                RuntimeManager.StudioSystem.update();
            }
        }
    }
}

Thread t = new Thread(StudioSystemUpdater.Run);
t.Start();
1 Like

Hi,

Audio dropouts are be unexpected, as the FMOD mixer already runs on a separate thread, and will continue to produce output even when the Studio system isn’t updated.

That said, yes, it’s possible to call update on another thread, unless you’ve decided to modify the Unity integration to use FMOD_STUDIO_INIT_SYNCHRONOUS_UPDATE. I’d recommend reading over our White Paper on Threads and Thread Safety for more info.

Thanks for the reply. We don’t change init flags so FMOD should be creating its studio and mixer threads normally. I forgot to mention the dropouts are in editor. In builds, a few players experience a brief sound repeating glitch on loading screens, like audio isn’t able to progress.

My StudioSystemUpdater idea wasn’t successful because GC.Collect() stops all managed threads. Trying to make a C++ thread for this is definitely not happening, so we may just need to look more at allocating our game differently.

What surprises me most still is that this RuntimeManager.Initialize() line

studioSystem.initialize(virtualChannels, studioInitFlags, FMOD.INITFLAGS.NORMAL, IntPtr.Zero);

creates the C++ thread, but dropouts still happen in editor during GC.Collect(). I have no idea why an unmanaged thread would be consistently blocked every garbage collection like that.

The only real interaction between FMOD’s C++ threads and C# garbage collection would be the way FMOD handles strings, which often need to be passed between managed and unmanaged code. Most of this would be string lookups for things like event/parameter/etc. names. String usage from integration code is stripped out in builds in favor of using GUIDs, which may explain why you’re primarily observing the dropouts in editor. I’d recommend changing the Event Linkage to GUID in FMOD for Unity’s settings and seeing whether that makes a difference in editor.

If not, could I get you to save and upload a Unity profiler session to your FMOD Profile? You’ll likely need to use the Unity Profile Analyzer in order to do this.

Changing event linkage to GUID works, I’m not getting loading screen dropouts anymore in editor which is nice! Thanks for that. It makes sense that string marshalling code is affected by garbage collection. We switched to EventReference a few years ago so our assets have path and GUID in them, and even though a lot of the game dynamically generates event paths at runtime, it still results in an EventReference.

Happy to hear!

As for the small dropouts in build, if they do present enough of a problem, you may want to try turning off logging completely for builds and seeing whether that makes any difference. There may be logging calls coming from the mixer thread that would cause the garbage collector to get involved, and logging calls from within custom DSPs, if you’re using any, may also have a similar effect.