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

Cesium CPU usage #1865

Closed
mramato opened this issue Jun 26, 2014 · 20 comments
Closed

Cesium CPU usage #1865

mramato opened this issue Jun 26, 2014 · 20 comments

Comments

@mramato
Copy link
Contributor

mramato commented Jun 26, 2014

We talked offline about this yesterday so I figured I'd write up an issue for it. The default render loop in CesiumWidget and Viewer currently render at 60fps no matter what. Setting the targetFrameRate property can lower it, but is only useful in limited cases.

What we really want to do is only render if we actually need to. This will reduce CPU load and also help with battery life (for devices where that's an issue). Here's the "rules" @kring previously came up with.

We don't render unless:

  1. The simulation time has changed,
  2. The user interacts via the mouse, etc.,
  3. A property of something in the scene has changed, or
  4. New data is loaded (such as terrain tiles)
@pjcozzi pjcozzi self-assigned this Oct 6, 2014
@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 16, 2015

@mramato
Copy link
Contributor Author

mramato commented Feb 20, 2015

As mentioned in the above issue, using the visibility API might also help out here in some circumstances: http://www.smashingmagazine.com/2015/01/20/creating-sites-with-the-page-visibility-api/

@emackey
Copy link
Contributor

emackey commented Mar 2, 2015

I don't think the visibility API is the right answer here, as requestAnimationFrame already takes page visibility into account.

Question, what is the best approach to implement this? Would there be a flag on Scene or RenderState or Context or someplace that gets set "dirty" indicating that one or more of @kring's conditions have been met?

@kring
Copy link
Member

kring commented Mar 4, 2015

Agreed re: the visibility API. It doesn't help.

I think a dirty flag is a reasonable way to go. Ideally we would stop the requestAnimationFrame completely when nothing is changing. We currently do this in National Map. It works ok, but it's pretty hacky doing it outside of Cesium. For example, I've hooked loadWithXhr and TaskProcessor to fire the render loop back up when data is loaded or a Web Worker sends a response.

@emackey
Copy link
Contributor

emackey commented Mar 18, 2015

Ken Russell touched on this topic in a recent WebGL-dev post. The first part of his message covers something we already do (aggregate events before rendering), but the next part talks about a flag that indicates whether requestAnimationFrame has yet been called for the very next frame.

It's a little trickier in Cesium's case, since the requestAnimationFrame loop is handled all the way up at the widget level, and is optional even there, it can be handled at the app level (and some apps use custom render loops).

Ken writes:

in your event handler:

  • Do whatever computation needed to update the position of the camera
  • Check the flag to see whether a render is pending
  • If the flag is false:
    -- Call requestAnimationFrame to render the scene
    -- set the flag to true

In your requestAnimationFrame callback:

  • Draw the scene with the camera's current position
  • set the flag to false

So, adapting this to Cesium's needs, we might do something like this:

  • Add a read-only flag to Scene called nextFrameRequested, initially false.
  • Add a new Event to Scene called onRequestNextFrame.
  • Add a new public method called requestNextFrame to Scene. It checks the flag and if false, sets to true and fires the event.
  • At the end of Scene.render the flag is reset to false without any event.
  • CesiumWidget already has a useDefaultRenderLoop flag, which will remain but its implementation changes: When useDefaultRenderLoop is true, CesiumWidget will listen for Scene.onRequestNextFrame events to fire, and always respond to those events by calling Cesium.requestAnimationFrame to queue up the next call to Scene.render and friends.
  • If CesiumWidget.useDefaultRenderLoop is false, then it is the app's responsibility to listen for Scene.onRequestNextFrame and call Cesium.requestAnimationFrame in response, requesting its own app-specific render function to fire.
  • Various places in code throughout Cesium will need to call Scene.requestNextFrame, for example if the currentTime changes, if Tweens change things, if inertia moves the camera, if tiles load, if responses are received from web workers, etc.

Interestingly, this does not break backwards compatibility. Apps that relied on the default render loop will get the upgraded behavior automatically. Apps that had their own custom render loop will continue to have the old behavior: They will ignore the previously unknown event from Scene and simply call requestAnimationFrame immediately at the end of their own render code, keeping the render loop going at 100% but not causing any problems.

If an author wishes to upgrade such an app to the new system while keeping the app-specific custom render loop, the author removes the last line of their custom render loop where they call requestAnimationFrame, and pastes the removed line into a new event listener on Scene.onRequestNextFrame to cause their own custom loop to re-fire. After upgrading to the new system, they may need to add their own calls to Scene.requestNextFrame if they've manually changed primitives in the scene.

Old app code: (still works)

function myRenderLoop() {
    // custom code here
    cesiumWidget.render(); // this includes scene.initializeFrame().
    Cesium.requestAnimationFrame(myRenderLoop);
}
// Prime the pump.
Cesium.requestAnimationFrame(myRenderLoop);

New app code: (uses new behavior)

function myRenderLoop() {
    // custom code here
    cesiumWidget.render(); // this includes scene.initializeFrame().
    // Don't ask for next frame here anymore.
}
// Listen to events indicating that a new frame will be needed:
scene.onRequestNextFrame.addEventListener(function() {
    Cesium.requestAnimationFrame(myRenderLoop);
});
// Prime the pump.
scene.requestNextFrame();

@emackey
Copy link
Contributor

emackey commented Mar 18, 2015

Or we could encapsulate the event callback in Scene itself. There should only ever be one callback running at a time, so Scene could just be given a reference to it.

function myRenderLoop() {
    // custom code here
    cesiumWidget.render(); // this includes scene.initializeFrame().
    // Don't ask for next frame here if scene.frameRequestHandler is defined.
}
scene.frameRequestHandler = myRenderLoop;
scene.requestNextFrame();

This can't go down into Context because that's private. Can everything that needs to call Scene.requestNextFrame get access to the scene object?

@emackey
Copy link
Contributor

emackey commented Mar 18, 2015

@kring this would badly mess up your target framerate watchdog stuff I think.

@kring
Copy link
Member

kring commented Mar 18, 2015

Hey @emackey, you've pretty much described how it works in National Map. The main difference is we use the actual useDefaultRenderLoop flag instead of introducing a new one plus an event, since we're not concerned with maintaining the semantics of that flag.

You're right that it messes up the framerate watchdog stuff. Our (fairly lame, but effective enough) approach is to ignore the watchdog if the animation shuts down before the watchdog finishes its sampling window. The assumption is that a low frame rate would make it take much longer than the sampling window to get to the point where we can stop rendering.

@kring
Copy link
Member

kring commented Mar 18, 2015

Can everything that needs to call Scene.requestNextFrame get access to the scene object?

I'm not sure. But if not, Scene can hold an animation trigger object that is passed down to anyone that might need it.

@pjcozzi
Copy link
Contributor

pjcozzi commented Sep 15, 2015

@chris-cooper
Copy link
Contributor

Just wondering if there is any ETA on this issue?

@pjcozzi
Copy link
Contributor

pjcozzi commented Jul 12, 2016

@chris-cooper no update other than #3476 will make this a bit easier. If you have the time, contributions here are appreciated.

@willemvdg
Copy link

+1

@wallw-teal
Copy link
Contributor

@chris-cooper We have been extending ol3-cesium's AutoRenderLoop. It still isn't perfect, but it is much better than before.

@ggetz
Copy link
Contributor

ggetz commented Aug 7, 2017

@ggetz
Copy link
Contributor

ggetz commented Dec 8, 2017

I've posted our initial plans to tackle this issue on the forum here: https://groups.google.com/forum/#!topic/cesium-dev/E2XXB44zWew

Please give us your feedback, including any of your thoughts or use cases there!

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 21, 2017

For initial work here, please provide feedback on #6065.

@ggetz
Copy link
Contributor

ggetz commented Jan 10, 2018

Feedback also welcome for additional work in #6107.

@ggetz ggetz mentioned this issue Jan 12, 2018
@mramato
Copy link
Contributor Author

mramato commented May 10, 2018

Closing this since requestRenderMode was a success and as far as I know we don't have any additional short term plans.

@mramato mramato closed this as completed May 10, 2018
@cesium-concierge
Copy link

Congratulations on closing the issue! I found these Cesium forum links in the comments above:

https://groups.google.com/forum/#!topic/cesium-dev/cGXhVoUacMg
https://groups.google.com/forum/#!topic/cesium-dev/X32mc77CTic
https://groups.google.com/forum/#!topic/cesium-dev/nS_GzoZ4b5g
https://groups.google.com/forum/#!topic/cesium-dev/E2XXB44zWew

If this issue affects any of these threads, please post a comment like the following:

The issue at #1865 has just been closed and may resolve your issue. Look for the change in the next stable release of Cesium or get it now in the master branch on GitHub https://github.com/AnalyticalGraphicsInc/cesium.


I am a bot who helps you make Cesium awesome! Contributions to my configuration are welcome.

🌍 🌎 🌏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants