Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a FPS limit for unfocused and minimized windows to decrease CPU/GPU usage #2001

Open
Calinou opened this issue Dec 18, 2020 · 6 comments · May be fixed by godotengine/godot#47812
Open

Comments

@Calinou
Copy link
Member

Calinou commented Dec 18, 2020

Describe the project you are working on

The Godot editor 🙂

Describe the problem or limitation you are having in your project

The Godot editor already limits itself to 10 FPS when the window is unfocused. This helps decrease CPU and GPU usage significantly, whicn in turn saves power/battery and decreases heat/noise emissions.

However, exported projects do not have any kind of FPS limitations when unfocused or minimized. This is problematic as it harms the "default" experience for exported projects, in a way similar to #1923. We should aim to have a out-of-the-box experience as good as possible, even for casual/gamejam games.

Many other game engines offer this feature by default. Likewise, many AAA games also limit the game's framerate when the window is minimized or unfocused.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

By default, enable low-processor mode and limit the FPS of exported projects to a relatively low value when their window is unfocused or minimized. Engines that support this feature often default to 10 or 20 FPS.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Add a project setting application/run/max_fps_when_unfocused (integer), defaults to 10. If set to 0, the limit is disabled.

We can also add a second project setting application/run/low_processor_mode_when_unfocused, which defaults to true. This behaves the same as low-processor mode (skips redrawing if nothing has changed).

If this enhancement will not be used often, can it be worked around with a few lines of script?

Technically, yes, but the project will need to be modified for it (see below). Also, implementing this functionality with a script will not make this option easy to discover as it won't be standardized across all Godot projects.

The script below can be added in an AutoLoad:

3.x

func _notification(what: int) -> void:
	match what:
		NOTIFICATION_WM_FOCUS_OUT:
			Engine.target_fps = 20
			OS.low_processor_usage_mode = true
		NOTIFICATION_WM_FOCUS_IN:
			# `0` means "unlimited".
			Engine.target_fps = 0
			OS.low_processor_usage_mode = false

4.x

func _notification(what: int) -> void:
	match what:
		NOTIFICATION_APPLICATION_FOCUS_OUT:
			Engine.max_fps = 20
			OS.low_processor_usage_mode = true
		NOTIFICATION_APPLICATION_FOCUS_IN:
			# `0` means "unlimited".
			Engine.max_fps = 0
			OS.low_processor_usage_mode = false

Is there a reason why this should be core and not an add-on in the asset library?

It can technically be an add-on, but the point here is that this functionality must be built-in to achieve the desired goal (which is, decreasing CPU/GPU usage on any project made with Godot).

@jordo
Copy link

jordo commented Dec 18, 2020

I will also just note, that on mobile devices this should basically map to applicationWillResignActive on iOS and onPause on Android. I've commonly seen 4 fps targeted by other engines during these states.

@Jamesong7822
Copy link

@Calinou Could you please describe the workaround you mentioned? Testing multiplayer on separate exported exes always prove to be challenging for me as switching to the client exe would cause the server exe to lose focus and run in decreased fps.

@Calinou
Copy link
Member Author

Calinou commented Dec 23, 2020

Testing multiplayer on separate exported exes always prove to be challenging for me as switching to the client exe would cause the server exe to lose focus and run in decreased fps.

FPS limiting does not happen with exported projects by default. Right now, it's something you need to explicitly implement in your project.

To disable FPS limiting in the editor while it's unfocused, set Interface > Editor > Unfocused Low Processor Mode Sleep Usec to 6900 in the Editor Settings.

@alexfreyre
Copy link

It would be nice to have the option to "only update the viewport" if you browse or change something inside, so that the user, if you activate the option, the viewport is not redrawn until we make a change.

On the other hand, this function could be enabled by default if the editor is inactive, it does not make sense to redraw the viewport if the editor is minimized

@Calinou
Copy link
Member Author

Calinou commented Feb 14, 2021

It would be nice to have the option to "only update the viewport" if you browse or change something inside, so that the user, if you activate the option, the viewport is not redrawn until we make a change.

This is already supported and enabled by default in the editor. It's known as low-processor mode. This can be enabled in projects too, but is not enabled by default because it can make games less smooth. Also, not all games will benefit from low-processor mode in the first place since there needs to be a complete lack of animation to prevent redrawing.

@dueddel
Copy link

dueddel commented Jan 6, 2024

As I am working with the current Godot version 4.2.1, I just want to mention that the said Engine.* properties from the examples above seem to be slightly renamed and/or relocated.

Therefore the two lines:

Engine.target_fps = 20
Engine.low_processor_usage_mode = true

should be changed to:

Engine.max_fps = 20
OS.low_processor_usage_mode = true

To whomever it may concern, in addition I'd also like to share my own code for a non-game application that I am building for personal use. It's a time-tracking tool that is likely to be run in background (unless I decide to close it) and which only consists of Control nodes (namely UI components) and has no actual 2D or 3D graphics at all. Thus I wanted the tool to be very low on the CPU consumption end – especially when it's idling around in background after losing its focus (since my tool isn't doing anything when not being used, but by using the default settings it consumed quite a lot of CPU).

I made the following script energy_saver.gd and added it to the Autoload section of my Project Settings:

# scripts/energy_saver.gd
extends Node
##
## This Autoload class lowers CPU/GPU usage as soon as
## the application window loses focus by changing some
## of the project settings. Restores the original values
## as soon as the window is focussed again.
##


# These values make it reeeeally slow!! Adjust to your needs!
##
## The amount of microseconds to sleep between frames
## when application is idling in background.
##
const IDLE_SLEEP_MSEC: int = 500000
##
## The maximum number of frames per second to be used
## when application is idling in background.
##
const IDLE_MAX_FPS: int = 2


##
## Helper variable to keep track of original value for
## [member OS.low_processor_usage_mode_sleep_usec].
##
var low_processor_usage_mode_sleep_usec: int
##
## Helper variable to keep track of original value for
## [member Engine.max_fps].
##
var max_fps: int


func _ready() -> void:
	# first of all keep in mind original values as set in project settings
	low_processor_usage_mode_sleep_usec = OS.low_processor_usage_mode_sleep_usec
	max_fps = Engine.max_fps


func _notification(what: int) -> void:
	# check for window focus events
	match what:
		NOTIFICATION_APPLICATION_FOCUS_OUT:
			_idle()
		NOTIFICATION_APPLICATION_FOCUS_IN:
			_unidle()


##
## Lowers the CPU/GPU usage by changing some settings.
##
func _idle() -> void:
	OS.low_processor_usage_mode_sleep_usec = IDLE_SLEEP_MSEC
	Engine.max_fps = IDLE_MAX_FPS


##
## Resets the CPU/GPU usage by restoring the original setting values.
##
func _unidle() -> void:
	OS.low_processor_usage_mode_sleep_usec = low_processor_usage_mode_sleep_usec
	Engine.max_fps = max_fps

Please have in mind that in the above script I didn't set the OS.low_processor_usage_mode property, because I have already set it in the Project Settings of my own application. Feel free to change the code as needed.
Also have in mind that the const values that I have defined are exceedingly high/low which basically puts my application in a state of doing almost nothing. Adjust these values to your needs as well.

PS: Should I make that script a plugin maybe, instead of posting it here where it might not exactly belong to? 🤔

EDIT:
Since I have written the script and my comment in a night nearly without any sleep I just came back to it and would like to question my own decision of setting the Engine.max_fps property. I am not sure if this is useful at all (at least in my case), I somehow doubt it. 😅
The OS.low_processor_usage_mode_sleep_usec however has a huge impact on CPU consumption as far as I can tell (despite the fact that I've probably set it to an extremely high value being too pessimistic and which could also be a bit lower without harming the CPU at all). Long story short, play around with the properties and their values on your own. Take my code with a grain of salt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: In Discussion
5 participants