-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
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
requestAnimationFrame loop causes CPU hog #7670
Comments
My workaround: Finally (after more than 1 year), I realized that my scene only changes on user input. Therefore, I made my call to requestAnimationFrame() conditional on whether there was a user input in the last 2 seconds. If not, I would go into standby mode and not call requestAnimationFrame(). As soon as there's new action, I call render() again. Here's my code:
Super-ugly. But this solved my problem. The CPU usage drops from 2 cores with 100% each to almost nothing, after 2 seconds of no activity on my page. Your situation may wary, you may other factors other than user input that cause scene changes for you. But maybe it's a lot less than every 16ms, and more importantly, there may be phases of complete inactivity when you can turn things off entirely until some trigger. (In my case: mouse move) I hope this helps. I still consider this to be a band-aid and ugly, and I think this needs to be fixed in ThreeJS. |
I don't think this is a bug. There are many things that can change in a scene and if the engine had to keep track of everything it would affect performance. We concluded that the only person that knows when the scene needs to be rendered again is the user (you), and you've the ability to do that. |
As @mrdoob said, this is very application specific. Invalidating scene state to request a re-render is an insanely hard problem and any solution will be very error prone, kind of like cache invalidation. |
Btw, there are more sophisticated solutions out there, like mainloop.js, which is based on the principles of the fix your timestep series. It does not solve your problem, but gives you more control than requestAnimatonFrame does. |
I don't have this problem with SceneJS. Run the SceneJS examples, run the ThreeJS examples, and compare CPU load. Esp. at idle. So, it seems it's solvable. |
Can you upload the tests somewhere? |
The SceneJS examples are at http://scenejs.org/examples/ . Seems like I misspoke, though: I'm getting almost the same bad CPU usage with these SceneJS examples. My own workaround code is posted here in the 2. comment. You can see it live on http://www.labrasol.com . |
@benbucksch said
Why are you calling |
Sorry, but this is clearly still an issue. No webpage should continue to use 100% CPU forever. But all the ThreeJS examples do that. Most developers will just copy that and waste CPU. WestLangley:
I already answered that in comment 1: Because that's what the tutorial said. I followed the tutorial, which is what any reasonable developer will do when he starts using a library he doesn't know. There was no hint whatsoever that the render should be conditional on anything.
Huh? That's what I'm doing? That's what I wrote in comment 2? What's your point? My point is that this needs to be fixed in ThreeJS, or at the very least in the tutorial and the examples need to be fixed. |
@benbucksch See this fiddle for an example of how to render a static scene. |
PRs welcome 😉 |
I found a good way by watching only state changes if you're using React. The following sample code will stop the render animation loop if current status is not in running state. Since any state changes will trigger class MyWorkflow extends React.Component {
state = {
workflow: WORKFLOW_STATE_IDLE
};
componentDidMount() {
// create your scene
}
componentDidUpdate(prevProps, prevState) {
requestAnimationFrame(::this.renderAnimationLoop);
}
renderAnimationLoop() {
let isAgitated = (this.state.workflow === WORKFLOW_STATE_RUNNING);
if (isAgitated) {
requestAnimationFrame(::this.renderAnimationLoop); // 60 fps refresh rate
// start animations
} else {
// stop animations
}
this.renderer.render(this.scene, this.camera);
}
} |
My CPU usage is also near to 80% up to sudden spikes to 120% after few minutes with any WebGL demo. CPU fan is going like crazy in that moment. |
That is correct. However, that fact is not related to this bug report. See my last comment (from about 5 years ago, sadly). Using 100% CPU forever is bad, on any core. The sample code is teaching developers to do the wrong thing. |
@benbucksch I do not think there is a problem with the upper code. Animation loop is running for an hour if I will not move the mouse and camera, nothing is consuming whole processor. Just about 20-40%. My problem is when I start moving around the environment with camera, or looking for demos with too dynamic animations / rendering. I discovered WebWorkers during last night, together with Offscreen Canvas which is not supported by all browsers. I will experiment more with WebWorkers and offscreen canvas with some detection for when and how to start/stop animation loop. As my conditions are not very user based. |
That's the bug. That's way too much for a modern CPU, when you're not even interacting with it. FYI, I said it's using 100% of 1 core (!), not the whole CPU.
Please see my first few comments here. They have a fix. |
The bug here was: The lib should do this by default. Or at least the sample code should demonstrate this, as every developer needs that. It's never OK to hog the CPU like that. |
I choose ThreeJS because of control is on you. Maybe some game engines do this and some caching by default. Using your solution is good for some article about optimisations. Thanks for your code as an inspiration. |
I ran into the same problem -- didn't realize it would be using a lot of CPU even when the user was not interacting with the static scene. My remedy was to use an event listener on the controls. I wish the example was more like this (rather than using requestAnimationFrame): // within a useEffect hook
const render = () => {
renderer.render( scene, camera );
}
controls.addEventListener( 'change', render );
controls.update()
return () => {
controls.removeEventListener('change', render)
} |
@mrdoob : Would it be possible to update the tutorials, sample code and README, to reflect the above changes? |
My guess is that using just event listeners only works for 20% of the projects. |
The live is not exististing anymore. Could you also share your full code for the same |
Labrasol was a commercial project and doesn't exist anymore. I shared the relevant portion of the code in comment 2. |
Followup to issue #642
I've followed the tutorial that suggested to call requestAnimationFrame() and render in a loop:
This bug caused my page to hog 1-2 CPU cores - 100% CPU usage on these cores - forever, even if nothing at all is happening in the scene, as long as my webpage was open, for days. It drains battery, uses electricity needlessly, and spins up fans. Thus, causing noise pollution and indirectly environmental pollution. Needless to say, that's a (page) killer. I was so frustrated, I was seriously trying to rewrite everything using another 3D library. (SceneJS doesn't seem to have the same problem.)
I think this needs to be fixed in ThreeJS. If nothing else, to save all our CPU cores on all the pages that use ThreeJS.
The text was updated successfully, but these errors were encountered: