Unity FMODUnity.EventManager / OnValidate() Bug

I’m periodically getting hard crashes from FMOD in Unity that corrupt the game and force me to delete the Library/obj/Temp directories. It takes quite a while to rebuild the project so it’s a big deal.

Here’s the stacktrace from the editor:

FMOD Studio: Creating editor system instance
UnityEngine.StackTraceUtility:ExtractStackTrace () (at /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/StackTrace.cs:37)
UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])
UnityEngine.Logger:Log (UnityEngine.LogType,object)
UnityEngine.Debug:Log (object)
FMODUnity.EditorUtils:CreateSystem () (at Assets/Plugins/FMOD/src/Editor/EditorUtils.cs:234)
FMODUnity.EditorUtils:get_System () (at Assets/Plugins/FMOD/src/Editor/EditorUtils.cs:311)
FMODUnity.EventManager:UpdateCache () (at Assets/Plugins/FMOD/src/Editor/EventManager.cs:144)
FMODUnity.EventManager:AffirmEventCache () (at Assets/Plugins/FMOD/src/Editor/EventManager.cs:57)
FMODUnity.EventManager:get_Banks () (at Assets/Plugins/FMOD/src/Editor/EventManager.cs:802)
Audio.FMODBankList:ValidateBanks () (at Assets/Audio/FMODBankList.cs:32)
Audio.FMODBankList:OnValidate () (at Assets/Audio/FMODBankList.cs:24)
 
(Filename: /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/StackTrace.cs Line: 37)

Start importing Assets/Plugins/FMOD/Cache/Editor/FMODStudioCache.asset using Guid(6e88c41db8b074239b819ea54b77c5d6) Importer(-1,00000000000000000000000000000000)
Done importing asset: 'Assets/Plugins/FMOD/Cache/Editor/FMODStudioCache.asset' (target hash: '7cacb7f05c466b01649da2db2aa7886e') in 0.132647 seconds
Assertion failed on expression: 'IsTransformHierarchyInitialized()'
UnityEngine.StackTraceUtility:ExtractStackTrace () (at /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/StackTrace.cs:37)
FMODUnity.EventManager:UpdateCache () (at Assets/Plugins/FMOD/src/Editor/EventManager.cs:317)
FMODUnity.EventManager:AffirmEventCache () (at Assets/Plugins/FMOD/src/Editor/EventManager.cs:57)
FMODUnity.EventManager:get_Banks () (at Assets/Plugins/FMOD/src/Editor/EventManager.cs:802)
Audio.FMODBankList:ValidateBanks () (at Assets/Audio/FMODBankList.cs:32)
Audio.FMODBankList:OnValidate () (at Assets/Audio/FMODBankList.cs:24)

[/Users/bokken/buildslave/unity/build/Runtime/Transform/Transform.cpp line 1137] 
(Filename: /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/StackTrace.cs Line: 37)

Obtained 58 stack frames.
#0  0x00000106e9542e in TransformChangeDispatch::QueueTransformChangeIfHasChanged(TransformAccess const&)
#1  0x00000108139c92 in Transform::RebuildTransformHierarchy()
#2  0x00000103d3a462 in AssetHotreload()
#3  0x0000010570aef3 in AssetHotreload(core::hash_set<UnityGUID, core::hash<UnityGUID>, std::__1::equal_to<UnityGUID> > const&)
#4  0x0000010580fad8 in RefreshInternalV2(AssetDatabase::UpdateAssetOptions, ScanFilter const&, InternalRefreshFlagsV2)
#5  0x00000105801d31 in StopAssetImportingV2(AssetDatabase::UpdateAssetOptions, InternalRefreshFlagsV2, ScanFilter const*)
#6  0x000001056c9ebe in AssetDatabase::StopAssetImporting(AssetDatabase::UpdateAssetOptions)
#7  0x0000015e2df839 in  (wrapper managed-to-native) UnityEditor.AssetDatabase:StopAssetEditing () {0x7fbdac635d60} + 0x89 (0x15e2df7b0 0x15e2df8e0) [0x14ff24640 - Unity Child Domain]
#8  0x0000015e17763b in  FMODUnity.EventManager:AffirmEventCache () {0x7fbdaa4eb588} + 0xfb (0x15e177540 0x15e1776a4) [0x14ff24640 - Unity Child Domain]
#9  0x0000015e17642b in  Audio.FMODBankList:OnValidate () {0x7fbf5f5ff400} + 0x1ab (0x15e176280 0x15e176485) [0x14ff24640 - Unity Child Domain]
#10 0x0000014f7cf578 in mono_jit_runtime_invoke
#11 0x0000014f992525 in do_runtime_invoke
#12 0x0000014f992483 in mono_runtime_invoke
#13 0x00000107249ac7 in scripting_method_invoke(ScriptingMethodPtr, ScriptingObjectPtr, ScriptingArguments&, ScriptingExceptionPtr*, bool)
#14 0x0000010724364f in ScriptingInvocation::Invoke(ScriptingExceptionPtr*, bool)
#15 0x000001072433f7 in ScriptingInvocation::InvokeChecked(ScriptingExceptionPtr*)
#16 0x000001072cfc75 in SerializableManagedRef::CallMethod(Object&, ScriptingMethodPtr)
#17 0x000001071fc8bb in MonoBehaviour::CheckConsistency()
#18 0x000001055b34c5 in AwakeFromLoadQueue::InvokePersistentManagerAwake(AwakeFromLoadQueue::Item*, unsigned int, AwakeFromLoadMode)
#19 0x000001055b3316 in AwakeFromLoadQueue::PersistentManagerAwakeFromLoad(int, AwakeFromLoadMode)
#20 0x000001055118cb in PersistentManager::IntegrateAllThreadedObjects()
#21 0x0000010551266c in PersistentManager::LoadAndIntegrateAllPreallocatedObjects(PersistentManager::LockFlags)
#22 0x00000105510f3e in PersistentManager::ReadObject(int, AwakeFromLoadMode)
#23 0x0000010450f2aa in PPtr<EditorExtension>::operator EditorExtension*() const
#24 0x000001050d0a0a in IsPrefabInstanceWithValidParent(Object*)
#25 0x000001050667db in MergeAllPrefabInstancesDuringLoad(AwakeFromLoadQueue*, TransferInstructionFlags)
#26 0x00000106be8b71 in LoadSceneOperation::CompleteAwakeSequence()
#27 0x00000106be84c1 in LoadSceneOperation::CompletePreloadManagerLoadSceneEditor()
#28 0x00000106be7887 in LoadSceneOperation::IntegrateMainThread()
#29 0x00000106be9e6a in PreloadManager::UpdatePreloadingSingleStep(PreloadManager::UpdatePreloadingFlags, int)
#30 0x00000106bea8d1 in PreloadManager::WaitForAllAsyncOperationsToComplete()
#31 0x0000010511f633 in EditorSceneManager::RestoreSceneBackups(std::__1::vector<EditorSceneBackup, stl_allocator<EditorSceneBackup, (MemLabelIdentifier)118, 16> >&, EditorSceneManager::PlayModeChange)
#32 0x00000105129056 in EditorSceneManager::RestoreSceneManagerSetup(SceneManagerSetup const&)
#33 0x00000105129fe7 in EditorSceneManager::LoadSceneManagerSetup(core::basic_string<char, core::StringStorageDefault<char> > const&)
#34 0x0000010488453b in Application::FinishLoadingProject()
#35 0x000001083933e0 in -[EditorApplication applicationDidFinishLaunching:]
#36 0x007fff205870cd in __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__
#37 0x007fff20622b1c in ___CFXRegistrationPost_block_invoke
#38 0x007fff20622a9a in _CFXRegistrationPost
#39 0x007fff2055834e in _CFXNotificationPost
#40 0x007fff212c8bb8 in -[NSNotificationCenter postNotificationName:object:userInfo:]
#41 0x007fff22d9f7d0 in -[NSApplication _postDidFinishNotification]
#42 0x007fff22d9f522 in -[NSApplication _sendFinishLaunchingNotification]
#43 0x007fff22d9c6c1 in -[NSApplication(NSAppleEventHandling) _handleAEOpenEvent:]
#44 0x007fff22d9c317 in -[NSApplication(NSAppleEventHandling) _handleCoreEvent:withReplyEvent:]
#45 0x007fff212f4306 in -[NSAppleEventManager dispatchRawAppleEvent:withRawReply:handlerRefCon:]
#46 0x007fff212f4176 in _NSAppleEventManagerGenericHandler
#47 0x007fff2636f7f3 in _AppleEventsCheckInAppWithBlock
#48 0x007fff2636ef0e in _AppleEventsCheckInAppWithBlock
#49 0x007fff26367c23 in aeProcessAppleEvent
#50 0x007fff287e68a2 in AEProcessAppleEvent
#51 0x007fff22d969a0 in _DPSNextEvent
#52 0x007fff22d94cd5 in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]
#53 0x007fff22d87049 in -[NSApplication run]
#54 0x007fff22d5b24c in NSApplicationMain
#55 0x000001083d590f in EditorMain(int, char const**)
#56 0x000001083d5c19 in main
#57 0x007fff204b3f3d in start
Launching bug reporter
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QObject(0x7fcdcc771530), parent's thread is QThread(0x7fcdcc607030), current thread is Thread(0x7fcd6d50a400)

And this is what’s in FMODBankList for reference:

using UnityEngine;
using System.Collections.Generic;
using System;

#if UNITY_EDITOR
using FMODUnity;
using UnityEditor.SceneManagement;
using UnityEditor.Experimental.SceneManagement;
#endif

namespace Audio
{
    public abstract class FMODBankList : MonoBehaviour
    {
        // References
        public string SceneName;
        public List<SoundInspectorBank> banks;

#if UNITY_EDITOR
        void OnValidate()
        {
            if (PrefabStageUtility.GetCurrentPrefabStage() != null) return;
            SceneName = EditorSceneManager.GetActiveScene().name;
            ValidateBanks();
        }

        private void ValidateBanks()
        {
            // Get All Banks
            HashSet<SoundInspectorBank> allBanks = new HashSet<SoundInspectorBank>();
            HashSet<SoundInspectorBank> combinedBanks = new HashSet<SoundInspectorBank>();
            foreach (EditorBankRef bank in EventManager.Banks) allBanks.Add(SoundInspectorBank.Create(bank.Name));

            // Sanitize Current Banks
            if (banks != null)
            {
                foreach (SoundInspectorBank bank in banks)
                {
                    bank.Sanitize();
                    if (String.IsNullOrEmpty(bank.bankPath) || allBanks.Contains(bank))
                    {
                        if (combinedBanks.Contains(bank)) bank.Reset();
                        combinedBanks.Add(bank);
                    }
                }
            }

            // Add Any Missing Banks
            foreach (EditorBankRef bank in EventManager.Banks)
            {
                // Banks for Scene
                if (bank.Name.StartsWith(SceneName))
                {
                    combinedBanks.Add(SoundInspectorBank.Create(bank.Name));
                }
            }

            // Update Bank List 
            banks.Clear();
            banks.AddRange(combinedBanks);
        }
#endif
    }
}

Can someone help me figure this out? It’s brought development to a halt. It seems like accessing EventManager.Banks from OnValidate() is causing problems. How can I fix that?

Does there need to be a new function in EventManager:

        public static bool IsEventCacheReady { get => eventCache != null; }

to check in OnValidate():

void OnValidate() 
{
    if (!EventManager.IsEventCacheReady) return;
...
}

Any guidance would be greatly appreciated!

I haven’t been able to recreate any crashes by accessing EventManager.Banks from OnValidate(), and it seems like the eventCache is consistently in a valid state after EventManager.Banks is called.
Specifically the problem appears to be arising when calling UnityEditor.AssetDatabase.StopAssetEditing() after updating the eventCache, something that ocurrs when calling Banks for the first time. I found some references ([1], [2]) to the AssetDatabase causing crashes/freezes when called from OnValidate(), so there might be some internal Unity issues there.
Perhaps you could try preventing the asset database from refreshing during your OnValidate() call, something like:

void OnValidate()
{
   if (PrefabStageUtility.GetCurrentPrefabStage() != null) return;
   SceneName = EditorSceneManager.GetActiveScene().name;

   AssetDatabase.DisallowAutoRefresh();
   ValidateBanks();
   AssetDatabase.AllowAutoRefresh();
}

Otherwise, has adding your IsEventCacheReady property stopped the crashes?

Hey thanks so much for digging into this. I’m sure you’re right, that the asset database probably shouldn’t be used in OnValidate. That being the case, my workaround does seem to be working. I haven’t had that crash since I added it in.

Would you be willing to add that property or something equivalent in future releases?

Good to hear, thanks for letting me know! I will raise it with the Dev team and see what they think, it seems innocuous enough, with a few potential uses and a trivial implementation, so I’m sure they’d be happy to add it.

Awesome! That’d be great. I’ll post if it ever pops up again, but I think this was the issue.