Unreal Engine 5 Early Access – MetaSounds Performance Quick Fixes

Unreal Engine 5 Early Access – MetaSounds Performance Quick Fixes

These fixes are mainly to address performance issues related to MetaSounds on the first frame when told to play. For more information on the problem and profiling done to identify it, you can scroll down to after the fix. Note: MetaSounds is in Early Access and there is no doubt in my mind that the power and performance of MetaSounds will be exponentially better by the time UE5 is released officially. These fixes are for folks like me who are impatient and want to try out ideas that were just not possible to do easily in UE4.

The Fixes

1. Re-Enable Asynchronous Sound Building.

According to Aaron Mcleran, this used to be on but they ran into technical issues working on Valley of Ancients and had to turn it off temporarily to fix other issues. Now those issues are fixed and we can re-enable it.

//ENGINE OVERRIDE - Enabling Async Task - Updating Graph seems to cause issues when doing the builder async
//BuilderTask->StartSynchronousTask();
//BuilderTask = nullptr;
//UpdateGraphIfPending();
		
BuilderTask->StartBackgroundTask(GBackgroundPriorityThreadPool);
// ENGINE OVERRIDE END
MetasoundGenerator.cpp diff view

2. Set BlockRate to 50 to ease CPU cost of playing a MetaSound

au.MetaSound.BlockRate 50

3. A bit hacky – Cache the Metadata into a Map to reduce the overhead of searching through for the same type of Metadata.

//ENGINE OVERRIDE - Caching pins to reduce cost by half for processing.
TMap<FName, FMetasoundFrontendClassMetadata> CachedMap;
  
// Inject receive nodes for each transmittable input
for (const FSendInfoAndVertexName& InfoAndVertexName : SendInfoAndVertexes)
{	
	bool bSuccess = false;
	FMetasoundFrontendClassMetadata ReceiveNodeMetadata = FMetasoundFrontendClassMetadata();
	//UE_LOG(LogTemp, Warning, TEXT("TypeName %s"), *InfoAndVertexName.SendInfo.TypeName.ToString());
	if(CachedMap.Contains(InfoAndVertexName.SendInfo.TypeName))
	{
		bSuccess = true;
		ReceiveNodeMetadata = *CachedMap.Find(InfoAndVertexName.SendInfo.TypeName);
	}
	else
	{
		ReceiveNodeMetadata = FMetasoundFrontendClassMetadata();
		bSuccess = GetReceiveNodeMetadataForDataType(InfoAndVertexName.SendInfo.TypeName, ReceiveNodeMetadata);
		CachedMap.Add(InfoAndVertexName.SendInfo.TypeName, ReceiveNodeMetadata);
	}

	//ENGINE OVERRIDE END
MetasoundSource.cpp Diff view

4. Make sure to enable stream caching and force streaming on your audio files.

In project settings, enabling stream caching will ensure that audio files can be processed as fast as possible during runtime.
On your wav files you can force streaming and make them seekable to take full advantage of wave samplers in MetaSounds

The Problems (More Details) – Synchronous sound building and Slow Metadata Searching On Input

I have recently jumped into UE5 to test out MetaSounds for some cool ideas for Project MIX. First thing I noticed however, is that runtime performance is severely impacted by large hitches during play (Remember that UE5’s MetaSounds are super super hot off the press, nothing is set in stone and everything is likely to improve and change).

Here is the MetaSound I am using for example. Nothing too complex, but I am playing many of these at a time so its understandable that there would be some performance concerns.

When looking at the results from using stat startfile and stat stopfile. We can clearly see the trouble areas.
For more information on profiling performance you can go here.

When playing multiple sounds on the same frame (around 5-10), we see these massive spikes.
Processing Sources – 37.044 ms
FAsyncMetaSoundBuilder – 10.418 ms.

Image

The first thing was getting some great insider knowledge from Aaron Mcleran saying that FAsyncSoundBuilder was actually built to run asynchronously. (Fix is above for that)

When going through and switching it back to an Asynchronous task we now get this

What we see now is the FAsyncMetaSoundBuilder issue is now gone without any real delay issues even with using the metasounds with Quartz.

This now leaves Processing sources as our next big hitter. After a few hours of digging I was able to narrow it down to this function in MetasoundSource.cpp

Every time FindClassesWithClassName is run, it can hit the cpu anywhere from 0.5ms to 2ms depending on complexity of your input graph pins. Essentially if your Metasound has 100’s of inputs, you will see a cost of about 1ms per pin when the sound plays, which is something that is likely to be improved. FOR NOW up above, I have a bit of a hacky fix where we make sure to only run this function once per pin type. So if you have 10 float inputs, it will cost 1ms to gather the metadata instead of 10 ms.

The reason is it seems to have to re-execute a query in order to get the proper meta-data. This to me feels like it is something that will be improved upon since it doesn’t quite make sense why it needs to do that on-play.

After the hack fix in MetasoundSource.cpp listed above, we see this as a result.

You can see the largest spike now happens much more rarily and is MAX about 12.8ms. Still reallllly high but comparatively now at least the game is playable. Hopefully this helps other folks like myself who want to dive into some awesome new tech!

UE4 How to make an actor that can Tick In the Editor

UE4 How to make an actor that can Tick In the Editor

Why not use an Editor Utility Actor?

Editor Utility Actors don’t quite get you the exact functionality that seems useful enough for ticking in the editor via an actor. It will tick in the preview windows of blueprint and potentially lead to lots of head scratching as you realize that the blueprint you were working on is logging when you are trying to use it in other places. Also, there are times where you actually do want the same actor runtime as well (It’s rare but totally a valid case).

The Solve.. Make your own actor class!

In your custom actor class you can use this to add a bool that toggles being able to tick in the editor. This way whenever you need this you can just simply check it on and get going. It also is separated so that you can have different logic happen in the editor vs in-game. Most of the time I end up with 1-1 anyway but its nice to have the option.

header file

/** Allows Tick To happen in the editor viewport*/
virtual bool ShouldTickIfViewportsOnly() const override;

UPROPERTY(BlueprintReadWrite, EditAnywhere)
bool UseEditorTick = false;

/** Tick that runs ONLY in the editor viewport.*/
UFUNCTION(BlueprintImplementableEvent, CallInEditor, Category = "Events")
void BlueprintEditorTick(float DeltaTime);

cpp file

// Separated Tick functionality and making sure that it truly can only happen in the editor. 
//Might be a bit overkill but you can easily consolidate if you'd like. 
void YourActor::Tick(float DeltaTime)
{
	if (GetWorld() != nullptr && GetWorld()->WorldType == EWorldType::Editor)
	{
#if WITH_EDITOR
		BlueprintEditorTick(DeltaTime);
#endif
	}
	else
	{
		Super::Tick(DeltaTime);
	}
}

// This ultimately is what controls whether or not it can even tick at all in the editor view port. 
//But, it is EVERY view port so it still needs to be blocked from preview windows and junk.
bool YourActor::ShouldTickIfViewportsOnly() const
{
	if (GetWorld() != nullptr && GetWorld()->WorldType == EWorldType::Editor && UseEditorTick)
	{
		return true;
	}
	else
	{
		return false;
	}
}


Then when you are done you should be able to add the new BlueprintEditorTick to your event graph and get rolling!

Final Notes

This is pretty dangerous if you aren’t mindful with what nodes you use in here. Try not to do things like Add Component or any other nodes that would spawn objects into the world or if you do make sure you store them and clean them up. Delays might work but they could be a bit odd as well. Just be aware that it can be fragile at times so make sure to give unreal some cake beforehand… ❤️

Implementing Undo in UE4 Editor Utility Widgets and Blueprints

Implementing Undo in UE4 Editor Utility Widgets and Blueprints

This image shows an example of how to setup an undo-able function call in unreal 4’s blueprints. This was reported as a bug and marked “by design” simply because you can do it 100% controllable and custom in blueprints

Begin Transaction

Starts the undo stack and allows for anything after this call to be grouped into 1 undo operation.

Transact Object

Every object you want to be undo-able has to be added by using the Transact Object node. Then you can do whatever you want to the object and it will properly go back to whatever it was before doing anything to that object.

End Transaction

Closes the gates and stops recording anything else to the undo stack. Which then will allow you to finally undo properly you custom tool’s operation.

This was a bit tricky to find since typing the word “Undo” in blueprint doesn’t give you much. Under the hood though everything undo-based is actually referred to as the Transaction System. Which is why these nodes are called Transaction Nodes.

That is all, hope this helps and Have a great day!

Unreal 4 Quick Tip – Shift Dragging Comments.

Unreal 4 Quick Tip – Shift Dragging Comments.

How the crap did I not know this? You can move comments 2 different ways. Group or non-grouped movement.

Shift+Drag changes it for you on the fly!

This.. this.. dammit. You know, it’s the little things like this that I miss all the time and I am just so happy to know about it now. Did you know?

Have a great day <3