What is Fortnite: Battle Royale?
We recently released a new PvP mode in Fortnite: Battle Royale. This new game mode drops 100 players into a large 5.5 km^2 playable area to duke it out and see who will be the last player standing. The scale of these requirements presented the Fortnite development team with several challenges, which will be discussed here.
In the course of developing the Battle Royale game mode we’ve made many performance, memory, and workflow optimizations that not only benefit Fortnite: Battle Royale but every developer using Unreal Engine 4, especially those building games with similar requirements.
All of these improvements are already available in both Perforce and GitHub. Many will ship in Unreal Engine 4.18 this month with the rest shipping in 4.19.
Dedicated Server Performance
Our first challenge was to optimize the dedicated server so that it could handle 100 simultaneous players, maintain 20hz, and minimize bandwidth.
With every frame the server sends updates to all actors near that player. That means for each player we need to determine all relevant actors, figure out what has changed on those actors, and send a packet to that player with the differences. In order to minimize CPU time and bandwidth we need send the minimum updates needed to provide a good experience for players.
Much of this work was profiling and optimizing the game code but we also found many engine optimization opportunities along the way.
Here are some of the dedicated server optimizations we made for the engine:
- Batched level streaming RPCs to reduce the number of RPCs that need to be sent to clients when they connect. (Will be in 4.19)
- Make the OS socket buffer sizes configurable and raise them for Battle Royale. This prevents simultaneously connecting clients from overflowing these buffers resulting in excessive server load. (Will be in 4.19)
- Reduce bandwidth for CharacterMovement RPCs when the character is not standing on any component, such as when jumping or falling. (Will be in 4.19)
- Added the ability to limit how many players receive updates from the server per frame. We set the limit to 25 in the lobby and 50 during gameplay. (Will be in 4.19)
- Limit the rate at which clients send movement updates to the server. Prevents clients running at high framerates from causing excessive load on the server. (Shipped with 4.17)
- Added an option to not replicate client ping times to other clients as it results in a lot of network traffic when there are many players. (Will be in 4.19)
- Removed several memory allocations during replication in FArchive::SerializeIntPacked and changing how CompatibleChecksum is calculated. (Will be in 4.19)
- Switched property type comparisons from strings to FNames for speed during replication. (Will be in 4.19)
- Added a way to view a dedicated server's network stats on a client in real time to more easily see stats for cloud-hosted servers. (Will be in 4.19)
- Changes to the ability system to better account for relevancy, which greatly reduces replication cost when using the ability system. (Will be in 4.19)
Building and Rendering a Large Map
The map in Fortnite: Battle Royale has a playable area of 5.5km^2. You can see the entire map at once when parachuting in and we wanted to support long view distances during gameplay. We knew that we needed to optimize our level-of-detail solution to make that possible.
We used the Hierarchical LOD (HLOD) feature in the engine, backed by Simplygon, to combine regions of the map into single low-poly meshes that could be drawn in a single draw call when viewed from a distance. Those tools already exist – we use them in Paragon – but we needed to make changes to allow our artists to be more efficient.
The way the map was broken up so that artists could collaborate on it didn’t mesh well with our HLOD tools. We made some changes to HLOD to better support that workflow and added a commandlet to rebuild all HLODs in the map that could run overnight rather than requiring artists to rebuild HLODs locally. (Will be in 4.19)
Shipping on Console
The extended view distance and player counts of Battle Royale presented several performance and memory challenges on console requiring us to make improvements, particularly relating to memory. Much of this work was game-side content optimization but we also made a number of improvements to the engine.
Here are some of the console improvements, which will ship in 4.18:
- [XboxOne + PS4] Improved our low-level memory tracking tools to better identify potential memory optimizations.
- [XboxOne + PS4] More efficient volume texture updates, which reduced peak memory by 240MB+.
- [XboxOne] Added options for different render target layouts to maximize bandwidth utilization on the GPU depending on what rendering features are enabled.
- [XboxOne] Reduced memory overhead in D3D12 descriptor heaps, and saved 120MB.
- [XboxOne] Allocated and freed render targets on-the-fly to reduce memory usage by 100MB+.
- [PS4] Optimized how we handle the texture streaming and defragmentation pools, saving 300-400MB.
While working on Battle Royale we identified some issues with input latency in the engine that particularly affected 30Hz games. We were able to make improvements to thread synchronization, reducing latency by around 66ms (the reduction will be around half that in a 60Hz title) to address this problem. These changes make a noticeable improvement to the feel of the game, making it more responsive and easier to aim. (Will be in 4.19)
More to Come!
These are just the engine improvements that came out of shipping the first version of Fortnite: Battle Royale. Having had this experience, we’ve identified further improvements we’d like to make, especially related to level streaming and editor performance when working with very large maps, that will make our game teams and our users across the industry more efficient.