As the Technical Lead for Content Support on VALORANT at Riot Games, Marcus Reid works on an engineering team responsible for the game’s engine, as well as supporting non-engineering content developers (designers, artists, etc.) across the wider VALORANT development team. Before joining Riot Games in 2017, Marcus worked at The Coalition for two years as an engineer on Gears of War 4 and Gears of War Ultimate Edition.
EDITOR'S NOTE: You can now check out our INSIDE UNREAL livestream replay featuring Riot's Marcus Reid right here.
Hello, I’m Marcus Reid, Technical Lead for VALORANT’s Content Support team. On June 2, we launched our five-versus-five character-based tactical FPS. Our team relies on Unreal Engine to provide a solid baseline that allows us to focus our energy on the game’s key differentiators. We made significant, targeted improvements to the engine for our scenarios, and regularly upgrade to new engine versions to make best use of features from Epic and the community. This approach allows our relatively small engineering team to support a large number of content developers building the game in editor. We spend most of our programming cycles developing gameplay and service capabilities rather than building functionality already provided by Unreal Engine.
Many of our larger engine modifications are motivated by VALORANT’s strict performance requirements. We measure client performance across various hardware configurations and evaluate server performance in terms of our target tickrate and concurrency (in other words, games per core). The following examples detail two areas of the engine we’ve modified in pursuit of performance goals.
Using "Dismiss," Reyna is rewarded for a kill with momentary invisibility and can flank or flee foes.
Customizing the renderer for low-end GPUs
Our minimum spec client targets 30 FPS (33.3 ms frametime) on the Intel HD Graphics 4000, an integrated GPU that launched in early 2012. We must maintain competitive integrity at different quality setting levels that players adjust for better frametimes. It’s important that different configurations do not impact readability or clarity in the game’s gunfights.
Meeting these goals necessitates stringent art budgets for every piece of content in the game as well as major artist effort and creativity to make the game look great without degrading minimum spec performance. Our artists are critical partners for hitting performance targets that are owned by our engineering team.
We made big investments in the engine’s rendering layer; a non-exhaustive list includes:
Custom forward renderer based on Unreal Engine’s mobile rendering path.
Our implementation predates the forward shading renderer added for desktop in UE4.14.
Unique base pass shaders authored to support just the capabilities we need with as few instructions and texture samples as possible.
Base environment material’s pixel shader uses just 41 instructions and one texture lookup when material quality is set to low.
A small number of capabilities normally restricted to stationary and movable lights ported to the static lighting path with minimal overhead.
Example: Each map has a single, static directional light that’s capable of casting dynamic shadows on first-person arms and weapons.
VALORANT’s gameplay environments only use static lighting.
Various tweaks for decals, including culling based on facing as well as occlusion culling with custom visibility queries, simplified vertex fog support, and custom lighting contributions.
Aggregate Z Prepass Mesh baked by the build for each map consisting of simplified geometry for major chunks of the map (walls, floors, and so on.) drawn to depth instead of the normal depth pass.
Our measurements show substantial improvement with the aggregate mesh compared to individual mesh depth draw calls in our scenes.
This approach may be deprecated in favor of automatic draw call merging from GPU Scene in the future—we’re always excited when we can replace a customization point with new engine capabilities supported by Epic.
The combination of our engine modifications and artist effort keeps us at 30 FPS or better on our minimum spec. These optimizations also mean that machines with modern GPUs often observe full-frame render times of just one to three ms.
Agile and evasive, Jett launches daggers from her "Blade Storm" for a counter‑attack.
Reducing animation overhead on the game server
VALORANT’s game servers run single-threaded at 128 ticks per second with three games per CPU core, making each server instance’s frametime budget just 2.6 ms. Reaching this target requires a ton of optimization across the game, and character animation provides a good example of the work involved in one area.
The game server skips animation for all skeletal meshes not involved in hit registration. When combat isn’t possible (For example, during the weapon-purchasing phase of each round), animations are disabled entirely.
To accurately check for hits, any actor that can be hit by weapon projectiles must evaluate animation and set correct bone positions. When a gun is fired, the game server rewinds the server simulation to the point in time where the client fired, accounting for network latency and client/server frametimes. Rewinding interpolates between past bone positions for sub-frame server time offsets and then checks for hits against targetable actors to compute damage. After checking for hits, the rewinder restores the server simulation to the present time and proceeds onward.
Measurements show animation updates and evaluations on one of our characters can take up to 0.1 ms. If we animate all 10 characters every server frame, nearly 40 percent of our total frame budget is gone. Most of the cost comes from the evaluation step; the update step is cheap by comparison.
Initially, we tried rate-limiting animation to every few frames and capturing snapshots of final bone positions after each evaluation. Rewinds always force an animation evaluation and snapshot for the current frame, and we interpolate between the two closest snapshots for hit testing.
Next, we added logic to prune unnecessary rewinds by excluding actors that couldn’t possibly be hit by a shot. This eliminates the forced animation evaluation for irrelevant actors. To determine relevance, the server sends a sphere cast from the player firing down their weapon’s aim vector with a sphere that’s sufficiently large to capture any recent movement and weapon error. Any characters that collide with the sphere are then rewound and evaluated.
Cypher, who is running, must be rewound to evaluate whether the firing player’s shot hit. Sova is standing far enough away that the server does not need to fully evaluate Sova’s bone positions to determine if he could be hit.
By pruning rewinds, we’re only performing the on-demand animation evaluation for characters that could be hit, but we’re still paying for periodic evaluations to create snapshots of bone positions for all characters. To remove those evaluations, we run the update step and snapshot the inputs to each animgraph node rather than snapshotting the final bone positions. We can then rewind the animgraph to an arbitrary point in the past and evaluate the graph to get bone positions.
Snapshotting animgraph node inputs is a hefty engine modification as it requires understanding the necessary state for each type of node, but the speedups are worthwhile for our scenario. With all of these optimizations in place, we now spend an average of 0.3 ms per server frame processing animation.
Unreal Engine provides fantastic tools and a set of solid capabilities that serve as VALORANT’s foundation. Our engineering team doesn’t spend time reinventing the wheel, and our content developers are highly productive with the editor. Unreal Engine enables rapid iteration on parts of our game that make it special.
Download Unreal Engine today!
Get the world’s most open and advanced creation tool.
With every feature and full source code access included, Unreal Engine comes fully loaded out of the box.