Loading banks + addressable issues

Before addressables i think i used Bank loader mono. It worked fine, and fast.

So I wanted to switch to addressables because I want to load banks and audio tables from remote yet the problem starts without even remote banks being loaded. All loading banks below are local!

I switched to proper import type for .bytes and strings.bytes.

I’m trying to load using Addressable Asset Databse option. Works fine.

I’m switching to Use build. Now LoadBank Master.bytes hangs main thread and loads the same banks for like 5 seconds.

There is no logs when it hangs.

Why IDK. I tested different groups etc to no effect.

I still use loading .strings with LoadBank. Since it doesn’t hang.

I found out it’s default blocking. Maybe issue? I guess it somehow was non blocking when I didn’t use Addressables?

There is no API for loading bank with non blocking so i have to dig…

Ok so I’m switching out the LoadBank to loading in memory. RuntimeManager.StudioSystem.loadBankMemory

Now loads in two frames my master bank.

Ok solution solved? Well not yet

Well It works using Use Existing Build but then I switch to Asset database and more errors.

This is using AssetDatabase.

I don’t want to go back to Runtime.LoadBank if it means having to wait 5 seconds every press play.

Solution?

unity 6000.1.6f

private async UniTask<bool> load_bank_from_asset_reference_non_blocking(AssetReference bankAssetRef, CancellationToken token)
        {
            AsyncOperationHandle<TextAsset> handle = default;

            if (bankAssetRef == null)
                return false;
            try
            {
                handle = bankAssetRef.LoadAssetAsync<TextAsset>();
                await handle;
                if (token.IsNullOrCanceled()) return false;

                Log.NoST($"[AudioManager] Loaded bank TextAsset from AssetReference. {bankAssetRef.Asset.name}");

                if (handle.Status != AsyncOperationStatus.Succeeded || handle.Result == null)
                {
                    Debug.LogError($"Failed to load Addressable TextAsset for {bankAssetRef.RuntimeKey}. Status: {handle.Status}");
                    return false;
                }
                else
                {
                    // NONBLOCKING
                    Bank bank;
                    var res = RuntimeManager.StudioSystem.loadBankMemory(handle.Result.bytes, LOAD_BANK_FLAGS.NONBLOCKING, out bank);
                    if (res != FMOD.RESULT.OK)
                    {
                        Debug.LogError($"FMOD loadBankMemory returned {res} for {bankAssetRef.RuntimeKey}");
                        return false;
                    }

                    LOADING_STATE state;
                    do
                    {
                        if (token.IsNullOrCanceled())
                        {
                            // unload bank to avoid partial resource leaks
                            try
                            {
                                bank.unload();
                            }
                            catch (Exception e)
                            {
                                Debug.LogWarning($"Exception unloading bank on cancel: {e}");
                            }

                            return false;
                        }

                        bank.getLoadingState(out state);
#if UNITY_EDITOR
                        Log.NoST($"[AudioManager] Loading bank to memory... {bankAssetRef.Asset.name}");
#endif

                        await UniTask.Yield();
                    } while (state == LOADING_STATE.LOADING);

                    if (state == LOADING_STATE.LOADED)
                    {
                        Log.NoST($"[AudioManager] Bank loaded from Addressables! {bankAssetRef.Asset.name}");
                    }
                    else
                    {
                        Debug.LogError($"Bank loading ended in state {state} for {bankAssetRef.RuntimeKey}");
                        // Unload to be safe
                        try
                        {
                            bank.unload();
                        }
                        catch
                        {
                            Debug.LogWarning($"Exception unloading bank on error: {bankAssetRef.RuntimeKey}");
                        }
                    }
                }
            }
            finally
            {
                if (handle.IsValid())
                    Addressables.Release(handle);
            }

            return true;
        }





bump

Is there no solution other than choosing this bugged 5 second hang with addressables LoadBank??


        public async UniTask load_banks()
        {
            var token = Global.LifetimeCts.Token;
#if UNITY_EDITOR
            var builder_name = SOEx.get_default_addressables_settings_builder_name_editor();
            if (builder_name == null) return;

            if (builder_name.ContainsEx("Asset database"))
            {
                await load_banks_by_RuntimeManager_synchronous(token);
            }
            else if (builder_name.ContainsEx("Existing build"))
            {
                await load_banks_by_memory(token);
            }
            else
            {
                Debug.LogError("[AudioManager] Not supporting Unknown Addressables builder: " + builder_name);
            }
#else
            await load_banks_by_memory(token);
#endif
            if (token.IsCancellationRequested) return;

            Log.NoST($"[AudioManager] Try find and assign buses");

            try_find_and_assign_buses().Forget();
        }


        /// <summary>
        /// Only works with an addressable existing build option
        /// <para>Does NOT work with an addressable asset database</para>
        /// </summary>
        /// <param name="token"></param>
        public async UniTask load_banks_by_memory(CancellationToken token)
        {
            // Load Master Strings
            var masterStrRes = await load_bank_from_asset_reference_non_blocking(MASTER_BANK_STRINGS, token);
            if (token.IsCancellationRequested || !masterStrRes) return;

            MASTER_BANK_LOADED_STRINGS_FLAG = true;

            // Load Master Bank
            var masterRes = await load_bank_from_asset_reference_non_blocking(MASTER_BANK, token);
            if (token.IsCancellationRequested || !masterRes) return;

            MASTER_BANK_LOADED_FLAG = true;

            // Load default VO banks if needed
            if (IsVoiceOverFlagsEnabled)
            {
                foreach (var assetReference in VOICE_OVER_INCLUDED_BY_DEFAULT)
                {
                    var voRes = await load_bank_from_asset_reference_non_blocking(assetReference, token);
                    if (token.IsCancellationRequested || !voRes) return;
                }

                VOICE_OVER_INCLUDED_LOADED_FLAG = true;


                foreach (var assetReference in ADDITIONAL_INCLUDED_AUDIO_BANKS)
                {
                    var voRes = await load_bank_from_asset_reference_non_blocking(assetReference, Global.LifetimeCts.Token);
                    if (Global.LifetimeCts.IsCancellationRequested) return;
                    if (!voRes)
                    {
                        return;
                    }
                }
            }
        }


        /// <summary>
        /// Preserves order
        /// </summary>
        public async UniTask load_banks_by_RuntimeManager_synchronous(CancellationToken token)
        {
            int totalLoadCount = 1 + 1 + (VOICE_OVER_INCLUDED_BY_DEFAULT?.Count ?? 0) + (ADDITIONAL_INCLUDED_AUDIO_BANKS?.Count ?? 0);
            int totalLoaded = 0;
            bool is_total_loaded() => totalLoaded >= totalLoadCount;

            //strings always fast
            RuntimeManager.LoadBank(MASTER_BANK_STRINGS, false, () =>
            {
                if (token.IsCancellationRequested) return;
                Log.NoST("[AudioManager] Loaded MASTER_BANK_STRINGS bank.");
                MASTER_BANK_LOADED_STRINGS_FLAG = true;
                totalLoaded++;

                // works like synchronous LAG / HANG with addressables use existing build :(
                // works like unsynchronous / NO LAG with an addressable asset database
                RuntimeManager.LoadBank(MASTER_BANK, false, () =>
                {
                    if (token.IsCancellationRequested) return;

                    Log.NoST("[AudioManager] Loaded MASTER_BANK bank.");
                    MASTER_BANK_LOADED_FLAG = true;
                    totalLoaded++;


                    if (IsVoiceOverFlagsEnabled)
                    {
                        var last = VOICE_OVER_INCLUDED_BY_DEFAULT.Count - 1;
                        var loaded_count = 0;

                        foreach (var assetReference in VOICE_OVER_INCLUDED_BY_DEFAULT)
                        {
                            if (token.IsCancellationRequested) return;

                            RuntimeManager.LoadBank(assetReference, false, () =>
                            {
                                if (token.IsCancellationRequested) return;

                                totalLoaded++;
                                loaded_count++;
                                Log.NoST($"[AudioManager] Loaded VOICE_OVER bank: {assetReference.Asset.name}");
                                if (loaded_count >= last)
                                {
                                    Log.NoST($"[AudioManager] on_reached_last_continue last");

                                    VOICE_OVER_INCLUDED_LOADED_FLAG = true;
                                }
                            });
                        }
                    }


                    foreach (var assetReference in ADDITIONAL_INCLUDED_AUDIO_BANKS)
                    {
                        if (token.IsCancellationRequested) return;

                        RuntimeManager.LoadBank(assetReference, false, () =>
                        {
                            if (token.IsCancellationRequested) return;

                            totalLoaded++;
                            Log.NoST($"[AudioManager] Loaded additional bank: {assetReference.Asset.name}");
                        });
                    }
                });
            });


            await UniTask.WaitUntil(() => is_total_loaded(), PlayerLoopTiming.Update, Global.LifetimeCts.Token);
        }

        private async UniTask<bool> load_bank_from_asset_reference_non_blocking(AssetReference bankAssetRef, CancellationToken token)
        {
            AsyncOperationHandle<TextAsset> handle = default;

            if (bankAssetRef == null)
                return false;
            try
            {
                handle = bankAssetRef.LoadAssetAsync<TextAsset>();
                await handle;
                if (token.IsNullOrCanceled()) return false;


                if (handle.Status != AsyncOperationStatus.Succeeded || handle.Result == null)
                {
                    Debug.LogError($"Failed to load Addressable TextAsset for {bankAssetRef.RuntimeKey}. Status: {handle.Status}");
                    return false;
                }
                else
                {
                    Log.NoST($"[AudioManager] Loaded bank TextAsset from AssetReference. {bankAssetRef.Asset.name}");


                    // NONBLOCKING
                    Bank bank;
                    var res = RuntimeManager.StudioSystem.loadBankMemory(handle.Result.bytes, LOAD_BANK_FLAGS.NONBLOCKING, out bank);
                    if (res != FMOD.RESULT.OK)
                    {
                        Debug.LogError($"FMOD loadBankMemory returned {res} for {bankAssetRef.RuntimeKey}");
                        return false;
                    }

                    LOADING_STATE state;
                    do
                    {
                        if (token.IsNullOrCanceled())
                        {
                            // unload bank to avoid partial resource leaks
                            try
                            {
                                bank.unload();
                            }
                            catch (Exception e)
                            {
                                Debug.LogWarning($"Exception unloading bank on cancel: {e}");
                            }

                            return false;
                        }

                        bank.getLoadingState(out state);
#if UNITY_EDITOR
                        Log.NoST($"[AudioManager] Loading bank to memory... {bankAssetRef.Asset.name}");
#endif

                        await UniTask.Yield();
                    } while (state == LOADING_STATE.LOADING);

                    if (state == LOADING_STATE.LOADED)
                    {
                        Log.NoST($"[AudioManager] Bank loaded from Addressables! {bankAssetRef.Asset.name}");
                    }
                    else
                    {
                        Debug.LogError($"Bank loading ended in state {state} for {bankAssetRef.RuntimeKey}");
                        // Unload to be safe
                        try
                        {
                            bank.unload();
                        }
                        catch
                        {
                            Debug.LogWarning($"Exception unloading bank on error: {bankAssetRef.RuntimeKey}");
                        }

                        return false;
                    }
                }
            }
            finally
            {
                if (handle.IsValid())
                    Addressables.Release(handle);
            }

            return true;
        }

Hi,

Thank you for sharing your solution.

We have a coding example for loading Addressables here: Unity Integration | Examples Async Loading which may have some helpful information.

I was not able to reproduce the issue following your code. Is it possible to share simple reproduction steps so I could look into the issue further?

The only thing I could take from example there is HaveAllBanksLoaded which would reduce nesting but not do anything to the problem.

There isn’t really much reproduction steps. My solution above I just copy pasted code for my preserved order in game case. Yet the problem occurs with just one master.bytes loading and without all the bool flags above.

I can only reiterate so maybe my language is more clear.

Editor only problem.

Master bank is not big. 300~ events

  1. master.strings.bytes + Addressables AssetDatabase option + LoadBank() = OK!
  2. master.strings.bytes + Addressables Use Existing Build option + LoadBank() = OK!
  3. master.strings.bytes + Addressables AssetDatabase option + loadBankMemory() = OK!
  4. master.strings.bytes + Addressables Use Existing Build option + loadBankMemory() = OK!
  5. master.bytes + Addressables AssetDatabase option + LoadBank() = OK!
  6. master.bytes + Addressables Use Existing Build option + LoadBank() = 5 second unnatural main thread hang.
  7. master.bytes + Addressables AssetDatabase option + loadBankMemory() = ERROR (ss above)!
  8. master.bytes + Addressables Use Existing Build option + loadBankMemory() = 2 frames loading OK!

The only thing Im doing differently in the solution is just avoiding 6 and 7 by checking that addressable option name.

It would be easier though if that wouldn’t be a problem at all.