Making the third-person content pack for Unreal Engine 5 into a multithreaded animation system is all about making things faster and smoother. It's like reorganizing how the animations work behind the scenes. Instead of using one road for all the animation work, we create several roads (threads) so that tasks can be done simultaneously. This upgrade boosts performance, making the game more responsive and immersive by tapping into the engine's full power. This blog post will explore converting the basic blueprint code snippets within the third-person animation blueprint into a multithreaded format. There are two ways to do this, property accessor for blueprint or animation proxy for c++. Today we will be using the property accessor.
What is the property accessor?
Property Access in Animation Blueprints lets you reach components and variables that are usually available only on the Game Thread. When you use Property Access, it captures a snapshot of the data, which you can then used on Anim Graph, Animation Node Function, a Blueprint Thread Safe Function, BlueprintThreadSafeUpdateAnimation. This method cuts down the workload your project handles on the Game Thread. Ultimately, this leads to a faster game and can lead to a smoother workflow.
Property Accessor return function
We will start by looking at the blueprint initialize animation, and convert this to property accessor.
The goal here is to be able to access the character movement component from a proper accessor. We do this by converting this chunk of blueprint in to a return function.
This is what the function might look like. Right clicking in the graph and searching "Property access" will return the correct node.
NOTE: The return function parameter must be called "ReturnValue" otherwise it will not show up in the property accessor drop down. It can not be "Return Value", it may look like my blueprint graph has it named with a space, but the editor will automatically add a space when you use capitals.
Thread safe functions
Now we have the more complex function out the way, let's look at how we can update are variables to be thread safe, allowing them to be called on the BlueprintThreadSafeUpdateAnimation and other thread safe animation functions.
Here is what our function looks like, let's have a look at some important notes on this.
We start by creating a standard function inside the animation blueprint, however we want to mark the function as thread safe inside the advance properties of the details panel.
Since we are thread safe, it's significant to understand that all functions and variable access inside the function must also be thread safe. This means that when accessing things, we must be calling thread safe functions inside the ABP or using the property access node when required. Since variable access is done by making copies this means that nodes like IsValid are not really true to their nature, meaning you are unable to call IsValid inside thread safe functions.
Below you can find the other finished thread safe functions using the property access node.
Here you can see both function use the property accessor calling our first made function, GetMovementComponent. If you are unable to see the function we made inside the property accessor, then have a look at the red marked note above.
Performance
Let's have a look at a synthetic benchmark using the tracing tools. Here are some small details about this benchmark.
For this, we will use 35 mannequins, evenly spaced out.
These mannequins will not move, allowing idle animation and logic processing to run.
The trace will last for 5 seconds.
Frame rate cap is 120.
Trace tool configuration will look at all results for "animation" across all threads, we are not limiting this to the game thread.
Third Person ABP
FParallelAnimationCompletionTask - 4.4 ms CompleteParallelAnimationEvaluation - 13.3 ms FinalizeAnimationUpdate - 290.3 ms USkeletalMeshComponent_TickAnimation - 1.5 ms UpdateAnimation - 1.7 ms
Third Person ABP - Altered
FParallelAnimationCompletionTask - 4.3 ms CompleteParallelAnimationEvaluation - 13 ms FinalizeAnimationUpdate - 268.9 ms USkeletalMeshComponent_TickAnimation - 1.4 ms UpdateAnimation - 1.6 ms
As we can see below, the majority of animation functions took less time, meaning in turn things are faster. In turn, each system is part of a chain of functions, causing positive performance changes, however this benchmark is synthetic and was only run a few times so your results may vary.
Final Note
The property access system is great once you wrap your head around it. This allows none c++ users to have noticeable optimization. In future will most likely be writing a second article similar to this one but instead of using the property access I will be using the animation proxy in c++, it will be interesting to compare all these results. I hope you have found this helpful.
Kommentare