I'm developing a 3D visualization tool based on Unity, targeting WebGL. The user rarely interacts with the 3D scene and when he does it's only about adjusting the camera like rotating or zooming the view. I disabled vSync (QualitySettings.vSyncCount
) and locked the frame rate to 30 (Application.targetFrameRate
) to reduce the CPU usage at least a bit while still having an acceptable level of responsiveness when the user moves the camera around. With 30 fps I get a CPU usage of around 30%. Considering lower-end hardware this seems still too high.
In order to further decrease CPU usage, I introduced two 'modes':
- Interactive: runs with 30 fps
- Idle: runs with 10 fps
I wrote some lines of code to switch between those modes depending on user interaction. So when an input is detected, the application uses more fps and after a certain amount of time without any interaction, the application uses less fps again. It works pretty well so far: The app uses 30% in interactive mode and 8% to 10% of the CPU while in idle mode.
Setting the idle frame rate to 1, CPU usage is reduced to 1.5% while in idle mode. This is really great. But as expected there's a problem. Basically I locked all the Update()
methods to be called only once per second. When the user interacts with the camera, the event will be processed with an expected delay of something around one second.
Now the question: How can I detect user input immediately while having the application run with a low frame rate and also immediately apply a new frame rate (say 30) without any noticeable delay?
Originally I wanted to have some sort of on-demand rendering. I've read some posts about how people render to a texture to reduce rendering cost in less interactive scenes. But setting the frame rate dynamically seems like a cleaner solution in my opinion. I also tried to use the FixedUpdate()
method to check for inputs and switch to interactive mode, but that didn't work either.
Note Also worth mentioning, this approach produces errors in the browser, indicating that the application frame rate should be 0 (not in terms of Unity's target frame rate but as in 'let the browser handle the frame rate'). While this might be a different question it's still related to this topic. Maybe someone has an answer for that as well!
Edit 1
As mentioned in the comments below, Unity provides the callbacks MonoBehaviour.OnApplicationPause
and MonoBehaviour.OnApplicationFocus
to detect whether the application is about to receive or lose focus.
However, the problem is this: My application nearly never loses focus. While this is a nice additional optimization it doesn't really help me too much. My problem is that the app is idle most of the time while also having focus. It's really just the user staring at the window most of the time.
Just to make some more sense: I'm targeting WebGL. Unity's sole purpose is to visualize some stuff while the user mainly interacts with some HTML controls. In the rare case he wants to interact with the 3D scene, I want to set the frame rate to say 30. The idea is "I don't re-render the scene too often as long as you don't interact".
To emphasize: In my most relevant use case the application never loses focus. This means, the only reliable indicator to determine when to switch between frame rate profiles is based on user input.
Sorry if I wasn't too clear about that :)
Edit 2
I posted this topic on the Unity Feedback Page. If you're interested, check it out and leave a vote. Maybe this request gets enough attention to get supported by the Unity developers.