Audiokinetic's Community Q&A is the forum where users can ask and answer questions within the Wwise and Strata communities. If you would like to get an answer from Audiokinetic's Technical support team, make sure you use the Support Tickets page.

Unreal Wwise 2022.1.1 stack overflow when reloading wwise asset data

+6 votes

We've followed the migration instructions (on both the Wwise project and the unreal side), everything went well. We generated the soundbanks, then Unreal got a notification that Wwise assets were going to be reloaded. Upon reload, the editor crashed in a stack overflow, in  

FWwiseResourceLoaderImpl::WaitForFutures(...)

This function seems not shielded against infinite (or at least, very long) recursion. Anybody was able to fix this? From what I see in the callstack when the stack overflow happens, the originating operation leading to this cascade of recursive calls is from the execution of the lambda defined in 

FWwiseFileHandlerBase::DecrementFileStateUse(...)

Did anybody encounter this? Did you solve it? Can Audiokinetic give any support on that or publish a hotfix? This is completely broken on our end (editor crashes at every launch now) and prevents us to migrate from 2021 to 2022...

EDIT
I tried fixing the WaitForFutures function like this:

void FWwiseResourceLoaderImpl::WaitForFutures(FCompletionFutureArray&& FutureArray, FCompletionCallback&& Callback, int NextId) const
{
   if (FutureArray.Num() <= NextId)
   {
      return Callback();
   }

   // Prevent unbounded recursion and stack overflows in case FutureArray contains a LOT of completed futures
   // (Indeed, Future.Next calls its callback in-stack if Future.IsReady !!)
   TFuture<void> Future;
   do 
   {
      Future = MoveTemp(FutureArray[NextId]);
      NextId += 1;
   } while (Future.IsReady() && NextId < FutureArray.Num());

   if (FutureArray.Num() <= NextId)
   {
      return Callback();
   }
   
   Future.Next([this, FutureArray = MoveTemp(FutureArray), Callback = MoveTemp(Callback), NextId/* = NextId + 1*/](int) mutable
   {
      WaitForFutures(MoveTemp(FutureArray), MoveTemp(Callback), NextId);
   });
}

It doesn't stack overflow anymore but I have three of these errors after loading:
> Error        LogWwiseResourceLoader    LoadEventResources: Could not load 1 prerequisites for Event \Events\Default Work Unit\SomeEvent (737333533). Unloading and failing.
> Error        LogWwiseResourceLoader    LoadEventAsync: Could not load Event \Events\Default Work Unit\SomeEvent (737333533) in language SFX (0)

Could it be that those three assets lead the asset loading in a state not foreseen by the original WaitForFutures function?

EDIT2
Those events do play correctly from the editor content browser though, so not really sure why the error messages if those events work.
Then, I proceeded to open another map... and it crashed again, this time in 

FWwiseResourceLoaderImpl::AddLoadSoundBankFutures(...)

in the FileExecutionQueue lambda. The LoadedSoundbanks reference is not valid anymore when the lambda gets executed.

Sigh.

asked Jan 6, 2023 in General Discussion by David T. (220 points)

3 Answers

0 votes
I am having the exact same issue (Wwise 2022.1.1 with unreal 5.1).
This happens both when closing a map in editor, and also when simply traveling from one map to another in our cooked build.

This issue was not present in 2022.1.0, which we used to migrate our data.
Now we had to update to 2022.1.1 to fix some crashes we had in Spatial Audio.

I'll try to make a homemade fix inspired by the one described in the original EDIT and will report back if I find something working better (since based on EDIT2, this does not seem to fix the whole issue).

If anyone from audiokinetic wants to jump in with a potential solution, that would be very appreciated.
answered Jan 17, 2023 by Olivier Belletete (190 points)
0 votes

Update:

I think I have a pretty good version of the fix.
Similar to David T. fix but instead of simply skipping over ready futures, it calls their next but without any continuation, preventing useless recursion:

void FWwiseResourceLoaderImpl::WaitForFutures(FCompletionFutureArray&& FutureArray, FCompletionCallback&& Callback, int NextId) const
{
    for (;NextId < FutureArray.Num(); ++NextId)
    {
        auto Future = MoveTemp(FutureArray[NextId]);
        if (!Future.IsReady())
        {
            Future.Next([this, FutureArray = MoveTemp(FutureArray), Callback = MoveTemp(Callback), NextId = NextId + 1](int) mutable
            {
                WaitForFutures(MoveTemp(FutureArray), MoveTemp(Callback), NextId);
            });
            return;
        }
        
        Future.Next([](int){});
    }

    return Callback();
}
 
answered Jan 17, 2023 by Olivier Belletete (190 points)
+1 vote

I actually found a solution to the EDIT2 issue too, it was a bug in my code where if the very last future is the only one that wasn't ready, I wouldn't wait for it. I just needed to add a check for that in the if after the loop:

   if (FutureArray.Num() <= NextId && Future.IsReady())
   {
      return Callback();
   }

 

Careful that there is yet another bug in the integration, if you are storing UAkAudioEvent assets in settings classes. If you do, you might run into an issue where the default object for your USettings class (the one returned by GetDefault<UMySettings>()) is created and loading its dependencies before the AkAudio module is loaded and initialized.
To fix this, you have to code yourself some delay loading of the UAkAudioEvent object that waits for the audio module to be initialized before trying to load the event data:

In AkAudioEvent.cpp:

...
void UAkAudioEvent::UnloadEventData()
{
   if (LoadedEvent)
   {
      auto* ResourceLoader = FWwiseResourceLoader::Get();
      if (UNLIKELY(!ResourceLoader))
      {
         return;
      }
      UE_LOG(LogAkAudio, Verbose, TEXT("%s - UnloadEventData"), *GetName());
      ResourceLoader->UnloadEvent(MoveTemp(LoadedEvent));
      LoadedEvent = nullptr;
   }
   // new branch:
   else if (DelayedLoadHandle.IsValid())
   {
      FAkAudioModule::OnModuleInitialized.Remove(DelayedLoadHandle);
      DelayedLoadHandle.Reset();
   }
}
void UAkAudioEvent::PostLoad()
{
   Super::PostLoad();

   if (bAutoLoad)
   {
      // new branch
      if (UNLIKELY(FAkAudioModule::AkAudioModuleInstance == nullptr || !FAkAudioModule::AkAudioModuleInstance->bModuleInitialized))
      {
         if (!DelayedLoadHandle.IsValid())
         {
            DelayedLoadHandle = FAkAudioModule::OnModuleInitialized.AddWeakLambda(this, [this]()
            {
               LoadEventData();
               DelayedLoadHandle.Reset();
            });
         }
      }
      else
      {
         LoadEventData();
      }
   }
}
...

And in AkAudioEvent.h:

...
private:
   void LoadEventData();
   void UnloadEventData();
   FWwiseLoadedEvent LoadedEvent;
   FDelegateHandle DelayedLoadHandle;  // new field
...

 

I hope Audiokinetic will fix those for the next release, it's pretty breaking.

answered Jan 18, 2023 by David T. (220 points)
Is it fixed?
...