Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

requestAnimationFrame [now] vs performance.now() time discrepancy

Assumptions: rAF now time is calculated at the time the set of callbacks are all triggered. Therefore any blocking that happens before the first callback of that frame is called doesn't affect the rAF now and it's accurate--at least for that first callback.

Any performance.now() measurements made before a rAF set is triggered should be earlier than rAF now.

Test: Record before (a baseline time before anything happens). Set the next rAF. Compare rAF now and actual performance.now() to before to see how different they are.

Expected results:

var before = performance.now(), frames = ["with blocking", "with no blocking"], calls = 0;
requestAnimationFrame(function frame(rAFnow) {
  var actual = performance.now();
  console.log("frame " + (calls + 1) + " " + frames[calls] + ":");
  console.log("before frame -> rAF now: " + (rAFnow - before));
  console.log("before frame -> rAF actual: " + (actual - before));

  if (++calls < frames.length) { before = actual; requestAnimationFrame(frame); }
});

// blocking
for (var i = 0, l = 0; i < 10000000; i++) {
    l += i;
}

Observations: When there is blocking before the frame starts, the rAF now time is at times incorrect, even for that first frame. Sometimes the first frame's now is actually an earlier time than the recorded before time.

Whether there is blocking happening before the frame or not, every so often the in-frame time rAFnow will be earlier than the pre-frame time before--even when I setup the rAF after I take my first measurement. This can also happen without any blocking whatsoever, though this is rarer.

(I get a timing error on the first blocking frame most of the time. Getting an issue on the others is rarer, but does happen occasionally if you try running it a few times.)

With more extensive testing, I've found bad times with blocking prior to callback: 1% from 100 frames, no blocking: 0.21645021645021645% from ~400 frames, seemingly caused by opening a window or some other potentially CPU-intensive action by the user.

So it's fairly rare, but the problem is this shouldn't happen at all. If you want to do useful things with them, simulating time, animation, etc., then you need those times to make sense.

I've taken into account what people have said, but maybe I am still not understanding how things work. If this is all per-spec, I'd love some psuedo-code to solidify it in my mind.

And more importantly, if anyone has any suggestions for how I could get around these issues, that would be awesome. The only thing I can think of is taking my own performance.now() measurement every frame and using that--but it seems a bit wasteful, having it effectively run twice every frame, on top of any triggered events and so on.

like image 780
Whothehellisthat Avatar asked Jul 13 '16 19:07

Whothehellisthat


1 Answers

I encountered the same issue on chrome, where calls to performance.now () would return a higher value than the now value passed into subsequent callbacks made by window.requestAnimationFrame ()

My workaround was to set the before using the now passed to the callback in the first window.requestAnimationFrame () rather than performance.now (). It seems that using only one of the two functions to measure time guarantees progressing values.

I hope this helps anyone else suffering through this bug.

like image 126
Sam Grad Oliver Avatar answered Oct 20 '22 10:10

Sam Grad Oliver