Intro
At Believer, with the aspirations and scope that we have for our game, it’s paramount to have a robust optimization process that’s built into content workflows. This starts in early project phases, to make sure our player experience is as smooth as possible, on as many devices as possible.
This post covers the core components of early project optimization strategies that I hope you can adapt or leverage for your own projects or help refine existing processes.
I’m Matt Canei, Principal Technical Artist at Believer. I’m responsible for optimization, UE5 best practices, shader libraries and other tech art-y stuff. I’ve been in the industry since 2010 and our project at Believer is my fifteenth consecutive Unreal Engine project. I’ve worked on just about every type of game you can think of and I feel a high degree of confidence that this type of strategy can work on just about any project.
Let’s dive in here to a bit of our process:
Game Content
We are going to bounce around the chart above and first start with Game Content and its considerations. When you’re starting a project, there are key components that drive optimization strategy:
- Scope
- What platforms do you want to target? PC, Mobile, Console? What type of hardware?
- Is this a single-player or multi-player game? How many players?
- What’s the genre of the game? Is it a sprawling open world or confined arenas?
- What is the general art approach? Hyper-real, stylized, in-between?
- Look Development
- In-engine tests on the ideal look of environments, characters, etc.
- These test scenes, often called beauty corners, can be deployed on target hardware later to see how close to frame targets we are at a baseline. This helps set expectations with art teams.
- Visual Pillars
- These are the 2-5 most IMPORTANT aspects of your art style.
- Your art/creative director needs to decide if its things like: High quality characters, high quality lighting and shadows, beautiful foliage, etc.
- This informs your performance team of both what DOES and DOES NOT need to be emphasized in game engine rendering features. For example, if characters are the primary visual pillar, we may be able to “trim fat” in the engine configurations for unrelated visual features so the baseline cost of our scenes are lower.
- Visual Parity
- This refers to how much variation in the game’s appearance creative stakeholders are willing to accept between the lowest-end and highest-end platforms.
- The closer they need to look, the less margin for error, the slower pipelines are, and the less of Unreal’s out-of-the-box performance features can be leveraged.
- I’m personally a fan of letting your low end be as low as it needs to be for maximized performance. I think the bigger your potential target audience is, with a wider range of hardware, the better your chances of success.
I’m going to come back to Scalability buckets…
30FPS vs. 60FPS
This is a HIGHLY debated topic, just go on Twitter and you’ll see lots of players fighting over which is better/acceptable. But first, I insist that we talk in terms of milliseconds and not FPS. The reason is that FPS is not a linear measurement and any given frame can take a variable amount of time to calculate; whereas, Frame Time is the amount of time it takes to render an individual frame and is precise for the purposes of optimization.
You can convert FPS to Frame Time and vice versa with the formula:
1000 / FrameTime = FPS; 1000/FPS = FrameTime
30 FPS = 33.33ms
60FPS = 16.66ms
The important thing to note about frame time is that your game’s entire content, code, etc. has to fit into 33.33ms or 16.66ms. I also think its really important to define these as MINIMUM acceptable performance numbers. Even if you’re targeting 60FPS it doesn’t mean you can’t offer players 120FPS modes, as long as your 60FPS spec is designed for a lower grade hardware.
Once you know some of the answers to the questions from the Game Content section, a path will typically present itself for whether 30FPS or 60FPS is more practical. Below is a cheat sheet for comparing some of the advantages or disadvantages:
My recommendation for this is to TEST, TEST, TEST! Try locked 30 tests, locked 60 tests, variable 60 tests and let players FEEL the difference. If its a better experience you may not have the 33.33ms option. Keep in mind that you do not HAVE to pick one Frame Budget for every platform…. More on that when we get to Scalability Buckets.
The last aspect of Frame Budgets is that at least in Unreal Engine, the frame is split up into different processing threads which make use of multiple cores of processors.
Game art is at the mercy of the GPU and Draw Thread on the CPU.
Game code, blueprints, simulations are at the mercy of the Game thread (CPU) and the game at large is at the mercy of whichever thread has the worst (highest) timing.
Once you’ve chosen your frame budget, with game content in mind, your performance team/tech artist may be ready to pre-configure the engine. This is done through Unreal Engine’s Scalability Buckets.
Scalability Buckets
Unreal Engine can’t possibly know the specifics of the type of game you may be making, so the engine is preconfigured in a generic way. This means there are rendering features in the engine that can likely be turned down to little/no visual affect to give your art teams more room to feature visual pillars and maximize the frame.
The engine has 4 scalability buckets that we map any given platform/hardware. This is based off of the scalability benchmarking system built into Unreal and the Device Profile system. Essentially, the engine breaks up all of its rendering features into sections where you can set CVAR
overrides for potentially thousands of rendering commands.
In your [ProjectFolder]/Config/DefaultScalability.ini
you’ll find something similar to below:
[ScalabilitySettings]
; Change engine defaults so a high end 2018 GPU will end up at a mix of Epic and High settings
; The resolution quality will need to be modified by users with 4k displays with the current autodetection system
PerfIndexThresholds_ResolutionQuality="GPU 18 115 350"
PerfIndexThresholds_ViewDistanceQuality="GPU 18 115 250"
PerfIndexThresholds_AntiAliasingQuality="GPU 18 115 500"
PerfIndexThresholds_ShadowQuality="GPU 18 115 500"
PerfIndexThresholds_GlobalIlluminationQuality="GPU 18 115 500"
PerfIndexThresholds_ReflectionQuality="GPU 18 115 350"
PerfIndexThresholds_PostProcessQuality="GPU 18 115 350"
PerfIndexThresholds_TextureQuality="GPU 18 115 250"
PerfIndexThresholds_EffectsQuality="GPU 18 115 350"
PerfIndexThresholds_FoliageQuality="GPU 18 115 250"
PerfIndexThresholds_ShadingQuality="GPU 18 115 250"
; Change some shadow settings to match the map content
[ShadowQuality@0]
r.Shadow.Virtual.ResolutionLodBiasDirectional=0
[ShadowQuality@1]
r.Shadow.Virtual.ResolutionLodBiasDirectional=0
[ShadowQuality@2]
r.Shadow.Virtual.ResolutionLodBiasDirectional=0
[ShadowQuality@3]
r.Shadow.Virtual.ResolutionLodBiasDirectional=-1.5
; Change some reflection settings to match the map content
[GlobalIlluminationQuality@0]
r.DistanceFieldAO=0
[GlobalIlluminationQuality@1]
; Enable DFAO when Lumen is off
r.DistanceFieldAO=1
[GlobalIlluminationQuality@2]
r.DistanceFieldAO=0
[GlobalIlluminationQuality@3]
r.DistanceFieldAO=0
[GlobalIlluminationQuality@Cine]
r.DistanceFieldAO=0
[ReflectionQuality@0]
r.Lumen.Reflections.Allow=0
[ReflectionQuality@1]
r.Lumen.Reflections.Allow=0
[ReflectionQuality@2]
r.Lumen.Reflections.Allow=1
r.Lumen.Reflections.DownsampleFactor=2
r.Lumen.Reflections.RadianceCache=1
[ReflectionQuality@3]
r.Lumen.Reflections.Allow=1
r.Lumen.Reflections.DownsampleFactor=2
r.Lumen.Reflections.RadianceCache=1
[ReflectionQuality@Cine]
r.Lumen.Reflections.Allow=1
r.Lumen.Reflections.DownsampleFactor=1
r.Lumen.Reflections.RadianceCache=0
; Engine scalability groups can be reorganized by using game-specific ini override syntax
; These changes disable the FoliageQuality group entirely and moves those settings over to ViewDistanceQuality
[FoliageQuality@0]
-foliage.DensityScale=0
-grass.DensityScale=0
[FoliageQuality@1]
-foliage.DensityScale=0.4
-grass.DensityScale=0.4
[FoliageQuality@2]
-foliage.DensityScale=0.8
-grass.DensityScale=0.8
[FoliageQuality@3]
-foliage.DensityScale=1.0
-grass.DensityScale=1.0
[FoliageQuality@Cine]
-foliage.DensityScale=1.0
-grass.DensityScale=1.0
[ViewDistanceQuality@0]
foliage.DensityScale=0
grass.DensityScale=0
a.budget.budgetMS=2.0
[ViewDistanceQuality@1]
foliage.DensityScale=0.4
grass.DensityScale=0.4
a.budget.budgetMS=2.5
[ViewDistanceQuality@2]
foliage.DensityScale=1.0
grass.DensityScale=1.0
a.budget.budgetMS=2.5
[ViewDistanceQuality@3]
foliage.DensityScale=1.0
grass.DensityScale=1.0
a.budget.budgetMS=2.5
[ViewDistanceQuality@Cine]
foliage.DensityScale=1.0
grass.DensityScale=1.0
a.budget.budgetMS=8.0
This lets you pre-configure all of your target hardware to scale the engine. For example, having less grass on mobile, or having lower quality shadows, while scaling textures up on Epic (Bucket 3).
Platform Name | GPU / VRAM | CPU | Memory | VRAM | Storage | Scalability Bucket |
PC - Recommended | NVIDIA RTX 4060 | Intel Core i5-12400F | 16GB | 8GB | SSD | High (2) |
PC - Max | NVIDIA RTX 4080 | TBD | 32GB | 12GB | SSD | Epic (3) |
PC - Mid | NVIDIA RTX 1650 | TBD | 16GB | 4GB | SSD | Medium (1) |
PC - Low | Intel UHD / RTX 3050 Laptop | TBD | 8GB | 4GB | Space-Only | Low (0) |
PC - Server | TBD | TBD | TBD | TBD | TBD | N/A |
The table above is an example of a potential hardware to scalability group mapping for a hypothetical game. If its a multi-platform game, I usually recommend that teams develop their content to the Recommended PC spec that has the largest market share GPU (usually an NVIDIA RTX XX60 for that era) and a common CPU. You can look at Steam Hardware survey, userbenchmark.com data and look at similar game’s Recommended and Minimum Specs to help guide your hardware selections.
For the other grades, it really depends on your studio and project. Typically Max is a high market share enthusiast grade GPU, Mid is a high market share previous gen GPU and Min spec depends on visual parity stance. I think its wise to give laptop gamers some love and configure that for a VERY low end laptop.
For flagship mobile and consoles, you should configure them for that hardware specifically. For modern consoles they map pretty well to one of your recommended or mid-level PC specs with some per-platform overrides (done in Device Profiles).
Conclusion
There is a LOT of detail that goes into performance strategy. This covers the general conversations and frameworks your team should be having in early development/pre-production to set yourselves up for success. In a future blog post I’ll cover the next part of the process:
- Content testing to find high-level limits (like tri counts and draw calls) and specific tests like “How many enemies can mobile handle?”.
- How everything leads to your team’s Content Budgets.
My last piece of advice is to TEST all of these strategic decisions/alignments as often as possible. Deploy your look dev content to different hardware, try it after pre-configuring your scalability settings, and come up with tests of representative areas to see how things run.
Some more resources I like to share related to this:
- https://dev.epicgames.com/documentation/en-us/unreal-engine/scalability-reference-for-unreal-engine
- https://unrealartoptimization.github.io/book/pipelines/
- https://courses.tomlooman.com/p/unrealperformance (I helped Tom with the curriculum for this course)
Hope this helps get your projects off to a smooth start. Remember to let your art teams cook - these are designed to be outer edge guidelines not “we have to be under budget every second of development.”