Mobile Friendly Frame Rate Setting (Deep Dive)
INTRODUCTION
This is the first in a series of articles covering some of the challenges & solutions used in the development of React. Whilst these articles might be specific to React, the concepts will be mostly applicable to other games and game development in general.
This article is also not overly technical, but it does assume a basic understanding of display\frame rates & vsync in the game development process.
Background
Modern mobile devices (phone & tablets) contain more powerful & feature rich SOC’s with each and every generation. And whilst gains are also made in battery technology, the more powerful phone hardware mostly cancels out these gains. So battery life is (and will likely continue to be) an important aspect for the foreseeable future. Most modern game engines (like Unity) also vastly simplify the process of utilising the underlying hardware, so it often leaves me pondering the question…
Why don’t most mobile games offer players some basic control of in-game visuals, much like consoles & PC’s?
I don’t necessarily mean low level control over all aspects of the visuals, for example, which GPU features should be used and at which levels. Just some easy to understand settings that produce noticeable results (trading quality for improved battery life) and allowing players some freedom of choice.
It’s also important that these settings must be understandable for the majority of players. If you were to ask most console & PC gamers what frame rate & vsync means, most will likely have a good idea. Ask the same question to casual gamers or consumers, most will likely have little idea.
Requirements
With all the above in mind, I condensed it to three key requirements…
The setting must work consistently (across all required platforms)
The setting must be high level and easy to understand (for the majority of players)
The setting must produce tangible benefits (i.e. improved visuals or battery life)
Notes
Before I start, there are a few things to note…
The following tests & implementation were carried out using various versions of Unity 2020.3.LTS and were (at the time of writing) accurate, as far as I am aware.
My games target Windows (UWP), Android & iOS. However, I also very much treat Unity itself as a platform (that is, the editor). Even though players will never use that version of a game, being able to run it on the development platform (equally as well as the target platforms) helps with the quality of the final product.
When I refer to (for example) 60hz displays, this not only means the display supports 60hz, but the OS was configured to update it at that rate.
So let’s understand a little more about the frame rate & vsync tools that Unity offers.
Target Frame Rate
Application.targetFrameRate instructs Unity to try and hit the specified frame rate. So a value of 60 would instruct Unity to try and run the game at 60fps. The key word here is try. The device may not be capable of running the game at the specified rate, be this due to hardware or game limitations (or both). It’s possible to specify a special value of -1, which basically means Unity makes the decision based on various platform & hardware factors.
It’s important to understand that setting this value to anything other than the refresh rate (or a integer divisible by the refresh rate) may produce unwanted behaviour. For example, setting it lower may result in less smooth visuals, whilst setting it higher may introduce screen tearing.
In testing I found that setting this value to -1 (on 60hz displays) resulted in large inconsistencies across the platforms. Some platforms ran uncapped, others at 30fps and some at 60fps. Far from ideal if a key requirement is consistency.
However, I did find setting this value to 60 (on 60hz displays) resulted in more consistency across the platforms, with the exception of Windows. One Windows device ran at 50fps whilst another 60fps.
NOTE: This could be a Unity bug and has been reported as such, but it’s important to know.
UPDATE: Unity has confirmed this as a bug and have now issued a fix.
But these more consistent results doesn’t mean this would be the best solution. Using targetFrameRate relies more on Unity’s internal logic (that varies platform to platform) that could be more sensitive to breaking bugs and future behaviour changes.
On-top of that, if I am to use this Unity setting I still need to know the display refresh rate (due to the huge variety of displays supported). Making this feature easy to understand is a key requirement, so do not want to concern the player with (or assume they have knowledge of) such concepts.
Display Refresh Rate
Resolution.refreshRate will allow us to determine the devices display refresh rate, without involving the player.
In testing I found that querying this property in Unity, Android & iOS (on 60hz displays) resulted in a value of 60. However on Windows it resulted in a value of 0.
NOTE: On reporting this to Unity it appears this feature is not currently supported on the Windows (UWP) platform, so again important to know. It does raise the question, what if this returned 0 on supported platforms due to a bug (in Unity, the operating system or device drivers). I’ll cover the workaround later.
UPDATE: Unity have now issued a fix.
So whilst it’s a good tool to use, again it’s not consistent across all platforms.
VSync
QualitySettings.vSyncCount instructs Unity to synchronise frame updates with display updates. In theory this should produce the most visually appealing results, as Unity is ultimately working hand-in-hand with the underlying hardware.
In testing, I found setting this value to 1 (i.e. update every vblank) resulted in 60fps for Windows, Android & iOS on 60hz displays, so as expected. However Unity ran uncapped as the editor does not support vsync.
Setting this value to 2 (i.e. update every other vblank) resulted in 30fps for Windows, Android & iOS on 60hz displays, so again as expected. However, Unity again ran uncapped.
So whilst these are the more consistent results that I had hoped for, they are still not perfect.
NOTE: On iOS this setting is ignored by Unity. Instead it forces frame updates on every vblank, skipping an update if the frame was not ready (due to targetFrameRate)
Consistency
Taking all the above into account, it’s plain to see that these three properties do not work consistently across all platforms. To therefore meet this key requirement, I am going to have to use different approaches on different platforms.
I settled on the following…
Platform | Approach | Comments |
---|---|---|
Unity | targetFrameRate | Unity does not support vSyncCount |
Windows | vSyncCount | Windows may have some issues with targetFrameRate and does not support refreshRate |
Android | vSyncCount | Android appears to work well with targetFrameRate and vSyncCount |
iOS | targetFrameRate | iOS does not support vSyncCount |
Using this combination (whether running in Unity or on Windows, Android & iOS) means React will almost always run at the expected rate, achieving the first key requirement of consistency.
Simplicity
I wanted to hide the concept of refresh rates, frame rates & vsync from players and instead, use non-technical terminology.
As a result, I settled on the following two terms for our frame rate setting…
Optimal (would run at the displays full refresh rate)
Reduced (would run at half the displays refresh rate)
This would mean achieving the second key requirement of making it easier to understand.
Performance
With Optimal running at the displays full refresh rate (i.e. 60fps on a 60hz display) visual performance will be smoother, but at the expense of battery life (due to Unity having to render more frames)
With Reduced running at half the displays refresh rate (i.e. 30fps on a 60hz display) visual performance will be less smooth, but at the benefit of battery life (due to Unity having to render less frames)
This would mean achieving the third key requirement of seeing tangible benefits.
Scalability
An added benefit of this approach is that it scales with display refresh rates, requiring no further code changes. This means current & future displays with higher refresh rates will still run correctly at Optimal & Reduced settings.
For example, a 90hz display will ran at 90fps & 45fps respectively. And a 120hz display at 120fps & 60fps respectively.
The exception here being the Unity platform itself, as it relies purely on targetFrameRate. It may be that the hardware and\or a game is not capable of running at much higher rates in the Unity Editor. Given that it does not support vsync, there is little that can be done about this.
Solution
Now that I’ve tested various scenarios and met key requirements, it’s time to implement the solution. This is actually the easy part.
I’ve chosen to encapsulate & centralise all common visual logic in a class called VisualManager
The first step was to define an enum for the two target frame rates…
Then I needed to implement a method to set the target frame rate by vsync…
NOTE: Unity will ignore targetFrameRate (if a non-zero vSyncCount is specified) but we set this to Unity’s special value of -1 for completeness.
And another method to set the target frame rate by the refresh rate…
NOTE: As discussed above, we can’t guarantee that refreshRate will return a valid value. In these rare situations, we need a default value and the widely adopted standard of 60 seemed as good as any.
I then added a method that will call the relevant frame rate method, depending on which platform the game is running on…
Finally I needed to add an in-game setting to allow players to toggle between the two values. How to-do this entirely depends on your UI style of choice and how UI logic has been implemented in your game, so out of scope for the purpose of this article.
I prefer using MVVM for the UI aspects. So when the View Model is notified of a change by the Model, I call the VisualManager.SetTargetFrameRate method (with the appropriate Model value) like so…
Or if you’re utilising UI control events (for example) it can be called literally like so…
And in keeping with the key requirement of making it easier to understand, notifications were added that slide in\out whenever the setting is changed…
Final Thoughts
So there we are, that is how I achieved a mobile friendly frame rate setting in React.
There are many different ways of achieving this in Unity, dependant on which platforms you are supporting and how much control you would like to offer the players.
I am not saying that this is the only way (and your testing\results may differ) but it worked for me and met all the key requirements. Hopefully it will help others who are looking to achieve the same.
If you would like to see the setting in action, please checkout the trial version of React. Once the loot fountain is emitting coins & powerups, it’s easy to see how smoothly they move when toggling the setting.
David
Article History
27th Oct 2021: Published