12

I am facing high CPU usage (30 to 40%) when calling recursively requestAnimationFrame, does anyone has good strategies to lower it down?

Simple example:

var canvas = document.createElement('canvas');
canvas.width = 100;
canvas.height = 20;

var canvasContext = canvas.getContext('2d');
document.body.appendChild(canvas)

var rafId;
function drawLoop(time) {
  canvasContext.clearRect(0, 0, 100, 20);
  canvasContext.fillRect(0, 0, Math.random() * 100 * 1.4, 20);
  rafID = window.requestAnimationFrame(drawLoop);
}

drawLoop();
Laurent
  • 977
  • 3
  • 13
  • 27
  • requestAnimationFrame() is _supposed_ to run as fast as possible, and thus use a lot of CPU. if you need fewer updates, use a timer to target a reasonable FPS rate instead. – dandavis Feb 08 '15 at 03:54
  • 1
    @dandavis: As far as I know `requestAnimationFrame()` is usually capped to 60 FPS and not "as fast as possible": https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame - with 60 FPS or more you should have plenty of time to for little updates on `` without maxing out CPU – Mikko Ohtamaa Feb 08 '15 at 04:28
  • 1
    @Laurent - try different browsers and devices. Your drawing code is so simple that it should only overpower very weak computer unless there are some issues (no HW acceleration, etc) – Mikko Ohtamaa Feb 08 '15 at 04:31
  • 1
    My 2-cents: Your resource usage is a bit high for modern devices. But, an older single-core, no-GPU, low memory mobile device with system+browser overhead plus rAF might pull as much as that. Anecdotally, I've seen weaker mobile devices get crushed by html canvas--but not with the simple code you have presented. – markE Feb 08 '15 at 04:49
  • @MikkoOhtamaa: yes, good point. I meant as fast as the device can go, which if the refresh rate is 60, is 60. i could have been more clear, but if the CPU is getting flooded, then OP is unlikely to be actually seeing 60 updates a second, and scheduling around, say 20FPS, would lower the CPU use... afaik, there is no way to adjust RAF's 'target FPS' yourself. – dandavis Feb 08 '15 at 05:10
  • @dandavis, Each call to rAF will fire within about 1/60th of a second, but it's easy to throttle an rAF loop using the automatically provided elapsed time argument. An rAF call that is dormant will cost trivially small CPU time. – markE Feb 08 '15 at 06:05
  • Still it's weird that it takes so much CPU, you can try it, simply open your dev tool on this page from Stack Overflow on Chrome, copy / paste the example code. Then open the Chrome Tasks Manager (Be sure to scroll to the bottom to see the animation). My CPU reaches 24~30% (mac book pro 15" last generation), if I wait like this a few minutes it starts to be very hot! – Laurent Feb 08 '15 at 11:53

1 Answers1

3

I cannot get this example to do anything to my CPU worth mentioning, but I did manage to get it down by employing these two methods. My CPU was running at about 4-5% running your snippet, by running save / restore on the context that shaved off 2%.Unsure why - because we haven't made any transformations. The latter example just uses the old hacker way of doing this by resetting the canvas.width - this wipes the entire canvas context each time - and should be more expensive - however it got this example down to 1.4%

var canvas = document.createElement('canvas');
canvas.width = 100;
canvas.height = 20;

var canvasContext = canvas.getContext('2d');
document.body.appendChild(canvas)

var rafId;
function drawLoop(time) {
  canvasContext.save();
  canvasContext.clearRect(0, 0, 100, 20);
  canvasContext.fillRect(0, 0, Math.random() * 100 * 1.4, 20);
  canvasContext.restore();
  rafID = window.requestAnimationFrame(drawLoop);
}

drawLoop();
var canvas = document.createElement('canvas');
canvas.width = 100;
canvas.height = 20;

var canvasContext = canvas.getContext('2d');
document.body.appendChild(canvas)

var rafId;
function drawLoop(time) {
  canvas.width = canvas.width;
  canvasContext.fillRect(0, 0, Math.random() * 100 * 1.4, 20);
  rafID = window.requestAnimationFrame(drawLoop);
}

drawLoop();

Now I would need to go into more performance exploration to find out why, or if it actually does anything at all.

However you could employ a different drawing technique, such as just moving a sprite or a mask back and forth over some bitmap data, that will make this much less hard for the renderer to handle. I will not post that here as it goes beyond the scope of this question.

Espen
  • 2,456
  • 1
  • 16
  • 25