7
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ You could just wrap your Update code inside an app-focus if block. Is there any reason to run your Update code when your app doesn't have focus? docs.unity3d.com/ScriptReference/… \$\endgroup\$ – John Hamilton May 29 '17 at 8:43
  • 1
    \$\begingroup\$ @JohnHamilton Thanks for your advice! That's another optimization worth implementing. However, I expect the application to stay focused most of the time. So I'm mostly interested in minizing the CPU workload while the app is running. \$\endgroup\$ – qCring May 29 '17 at 11:25
  • \$\begingroup\$ Just use a pause on focus loss and continue on focus, should be relatively easy to implement. Post your way of implementing this feature (as an answer) here so that it can be of use in the future :) \$\endgroup\$ – John Hamilton May 29 '17 at 12:36
7
\$\begingroup\$

I think render to texture is still your best bet. If you took a "screenshot" of what the camera sees and just display that texture the rendering cost should be cheap even at 30fps.

\$\endgroup\$
  • \$\begingroup\$ Thanks, I'll give this a try when I'm certain that adjusting the frame rate dynamically leads to a dead end. But you might be right though. This approach popped up on many forums so it seems to be the way to go. \$\endgroup\$ – qCring May 30 '17 at 15:33
1
\$\begingroup\$

So I thought about this, what if you were to just stop the camera from rendering again(Not disabling the camera) when the user is stationary? That way you could still have a frequent calls to the Update function so that user input is still responsive but the device wouldn't have a high resource consumption. After a quick search I found this. I haven't tried it yet, it's just an idea you could tryout.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.