Optimizing the game is one of the most essential and common tasks any game development team faces. No matter if it’s cross-platform game development, porting, or a PC exclusive — you need to make sure your game works well on as many device configuration variations as possible.
The essential point of the optimization process is discovering the areas of high risk. These areas vary platform wise. In order to discover those areas, developers perform the profiling process. Every platform has its own toolset for profiling, but today we’d like to talk about Unreal Engine 4 profiling tools.
The problematic area, also referred as a bottleneck, usually takes place in 3 places:
- Game thread
In order to define the bottleneck, launch the game in the non-shipping configuration and enter the START UNIT command. Now you can see how much time each thread takes to perform.
Test configuration is good for these purposes due to its ability to support some console commands for profiling and stats collection. The test configuration is also close to the shipping one. In case of issues with compiling the test configuration, you can also use the Development configuration.
The time in the Frame line reflects the time required to display one frame of your game. Due to both Game and Draw streams being performed before the frame displaying, Frame time is usually similar to one of these streams. GPU time reflects the time required for a graphic card to render a scene. These numbers are likely to be very close due to GPU time being synchronized with the Frame draw time. If the Game thread time is similar to the Frame time, the bottleneck is likely to be in the Game thread.
If the Draw time is similar to Frame time, the bottleneck is in the render thread.
If none of these numbers is close to the Frame time, the bottleneck is related to the graphic card.
Also, if none of the streams is similar to Frame, the bottleneck is in the async data loading.
If the issue is in the Game thread, the best performance analysis method is stats tracking. To start profiling, launch the console via the ~ button and enter the «stat startfile» command. To launch the console on mobile devices, touch the screen with four fingers.
Process approximately 10 seconds to get the acceptable average number from a big amount of frame cycles. Stats files would also work well due to them being able to track the periodic issues, But you won't be able to support 30 minutes + stats files because of their size.
As soon as you have a stats file for a necessary time frame, you can stop the process via «stat stopfile» command. The ue4stats file will be stored in the project folder at Saved/Profiling/UnrealStats.
Launch the UnrealFrontend from the UE4Editor folder to open the stats file. You can also open the Session Frontend tab in the editor from the Window menu. AS soon as you open the Session Frontend folder, switch to the Profiler tab inside. Now you can upload your ue4stats file.
The most important data is placed in the lower function tree. Open the GameThread and scroll down until you see the value with IncTime bigger than 2 ms and which doesn't have a lot of included functions (or even doesn't have them at all).
You should also pay attention to the Calls table that displays the average amount of function calls per frame. Ignore the CPU Stall, it only displays the expectation time, so we can't judge if the frame rate is limited or the bottleneck is not in the Game thread.
This is a real issue that took place in Fortnite. In this particular case, there was a lot of text that changed its size depending on the distance between the camera and the object. This caused thousands of the same lines of various sizes in the Unreal Engine UI system. To fix that, developers substituted the size change with a few discrete numbers for few distance types.
There are few ordinary narrow places you should check. FTickFunctionTask is very important because every actor and ticked component uses it. Decreasing the amount of constantly ticking components and actors is a great way to increase productivity.
If your game features the actors that should never tick and you use C++ code, you can add the following code to the actor constructor to turn down the tick:
PrimaryActorTick.bCanEverTick = false;
If it’s meant to tick eventually, use the following code:
PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bStartWithTickEnabled = false;
This will let you activate the tick via the SetActorTickEnabled function. You’ll also be able to decrease the tick frequency for less important actors or in some situations, like the one when the actor is far from the player or out of his view field. Unreal Engine has a significant manager that controls it, but you can create your own mechanism for more detailed controlling.
You should also take into account the BlueprintTime. You can find it in the list via the inclusive (coalesced) view. This unites the BlueprintTime values into a single line. if you pick BlueprintTime and then switch back to the Hierarchical, all the places that use Blueprint code will be selected. Now you can see what uses the time and in what particular Blueprints does that happen.
TickWidgets are also a pretty common thing to cause performance issues. If the TickWidget time value is big, you’re likely to display too many widgets simultaneously, or their hierarchy is too complicated, or maybe their parameters delegate is too complicated. For example, visibility parameters may be called multiple times per frame. So we need to keep them small and be transported when it’s necessary.
If your game features too many Skeletal Meshes, the SkinnedMeshComp Tick may be too big. Try decreasing the number of bones in the skeletals and simplify the Anim Blueprints. If you don't need to update the animation when you don’t see the SkeletalMesh, you can set the MeshComponentUpdateFlag parameter in the OnlyTickPoseWhenRendered. Do it carefully, because AnimNotifies won’t be processed for the non-visible meshes.
If you’re trying to discover what causes the periodic performance loss, you’d better find the pick at the timeline, pick the frames in it, and switch the view from Average to Maximum.
Unreal Profiler is a powerful and widely used tool. Of course, it can be limited by the platform's specification, high loads for the project, or incorrect data synchronizing. Specific platform profilers may help you with these issues. Epic Games has also added the new profiler called Unreal Insights that doesn’t cause that much load, provides more data and tracks time more accurately. WOuld you like us to tell you more about it in another article?