Adding a Movement FastPath
Timestamps:
- 0:00 - Intro. The "problem" of moving scene components.
- 2:25 - Showing the cost in Unreal Insights
- 5:12 - The fast path implementation (C++)
- 7:02 - Running the packaged build
- 7:40 - Comparing before & after in Unreal Insights
- 10:10 - Analyzing the custom Task in Unreal Insights
- 13:07 - Showing the WasRecentlyRendered() trick to skip updates
- 14:48 - Outro and takeaways
Target Audience: Everyone that does gameplay programming in C++ and needs to move components.
Moving SceneComponents in Unreal Engine is generally pretty slow. There are many tricks available to avoid this cost. One of those is adding a "movement fast path" or simply put...trimming the "fat" from the engine's MoveComponent calls. Other ways of avoiding this cost include animating on the GPU (using World Position Offset) or using Niagara to control positioning as (Mesh) particles instead of individual components. Sometimes you need the full fat MoveComponent to catch edge cases or simply because you use the features (NavMesh updates, Physics Updates, Transform Callbacks, etc.)
Covered in this lesson
- A custom movement function to handle an component move, it's children and updating any collision.
- Adding multi-threading to handle the movement.
- Custom cycle counters to profile the change with Insights.
- Custom CVARs (Console Variables) to A/B test the change.
- Additional tricks to reduce movement costs.
Insights
We use trace.screenshot to give us context of the gameplay. Make sure you use the task
trace channel when launching insights, eg. -trace=default,task
to make profiling the TaskGraph easier.
WasRecentlyRendered()
Added as an experiment to further optimize this movement logic. It's available on Actors and PrimitiveComponents and can be used in other situations too to skip certain logic when not recently seen on screen.
Tip: Depending on your game and how you use SignificanceManager, this can be used to completely skip spawning short lifespan particle effects such as muzzle flashes.
Keep in mind that Shadow Relevancy (eg. component was drawn for shadow rendering that frame) will consider it as recently rendered.
At this time it did seem that Nanite was having issues with this option although whether that was a local issue on my end is unclear at this time.
Using the TaskGraph
While using the TaskGraph to offload the GameThread and run the movementl logic on a workerthread is often a great way to gain performance, it may also cause worse performance due to potential overhead with using this system. Both on the GT itself and on the worker threads.
This is why it's often much more interesting to batch updates together. This could even be done using a simple ParallelFor (We have a lesson on that!). This should be relatively easy to set up for your update logic through a Subsystem or similar design.
0 comments