Closed Bug 1278408 Opened 8 years ago Closed 8 years ago

requestAnimationFrame() timestamp argument has jitter

Categories

(Core :: DOM: Core & HTML, defect)

48 Branch
defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla50
Tracking Status
platform-rel --- ?
firefox50 --- fixed

People

(Reporter: joliss42, Assigned: mchang)

References

(Blocks 1 open bug)

Details

(Whiteboard: gfx-noted [platform-rel-jQuery])

Attachments

(5 files)

The requestAnimationFrame() function passes a timestamp argument to its callback.

In Chrome, when an animation runs at 60fps, the timestamp values will be exactly 16.7ms apart. By contrast, in Firefox, the delta between successive timestamps has some jitter, varying randomly around 15-18ms.

I made a CodePen to illustrate this: https://codepen.io/joliss/pen/ZObOPm With Chrome, the green line is perfectly straight (save for frame drops), whereas with Firefox, the green line is jittery.

I'd argue that Chrome's behavior is worth copying here, because the frames will actually be drawn exactly 16.7ms apart on the screen, and it makes sense for the timestamp to reflect this. In practice, the jitter is a problem for tweening code like jQuery's $.animate, because they'd ideally like to calculate tweening values based on the timestamp argument. The jitter then causes animations to feel slightly choppy even at 60fps. Here's a discussion on the jQuery bug tracker: https://github.com/jquery/jquery/issues/3143
Attached image Chrome graph
Attaching a screenshot of the CodePen on Chrome v53.
Attached image Firefox graph
Attaching a screenshot of the CodePen on Firefox v48 (you can see jitter in the green graph).
Confirming the issue.

For me the results also highly depend on the graphics card used on my MacBook Pro: the integrated Intel Iris Pro 1536 MB behaves terribly and the dedicated AMD Radeon R9 M370X 2048 MB much better, see the screenshots. Other browsers don't get that worse on the dedicated card.

This might be a separate issue (if Firefox has hardware acceleration problems on Intel cards) but it's worth pointing out nonetheless.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Updated the related bug https://bugzilla.mozilla.org/show_bug.cgi?id=828189 as well, this might be a dupe of a 3y old bug.
The timestamp passed to nsRefreshDriver::RunFrameRequestCallbacks (which is the timestamp passed to all the callbacks) can come from several different places:

1)  When running off an actual timer, it's the TimeStamp::Now() call in the timer callback.
2)  When running off vsync, it's the timestamp that was passed to NotifyVsync().

So the first question, especially given comment 5, is whether in the cases where the jitter is observed we're running off vsync or a timer.  Vlad, do you know how Michał could determine that?  Or do we just always run off vsync in non-background tabs?
Flags: needinfo?(vladimir)
I think this is a dupe of https://bugzilla.mozilla.org/show_bug.cgi?id=828189 indeed. Should we continue the discussion there?
Bug 828189 predates the existence of the vsync codepath, I'm pretty sure.
(In reply to Boris Zbarsky [:bz] from comment #7)
> The timestamp passed to nsRefreshDriver::RunFrameRequestCallbacks (which is
> the timestamp passed to all the callbacks) can come from several different
> places:
> 
> 1)  When running off an actual timer, it's the TimeStamp::Now() call in the
> timer callback.
> 2)  When running off vsync, it's the timestamp that was passed to
> NotifyVsync().
> 
> So the first question, especially given comment 5, is whether in the cases
> where the jitter is observed we're running off vsync or a timer.  Vlad, do
> you know how Michał could determine that?  Or do we just always run off
> vsync in non-background tabs?

It should always be vsync.  The RefreshDriver itself will choose between active and inactive timers, and when vsync is in use, it'll create a vsync refresh driver timer for active tabs.  On Mac, we basically always use vsync (from gfxPlatformMac.cpp).  Can you post a screenshot or some values of the type of jitter you see?  Is it a full frame's worth, or just frames that take longer, etc?

That said, we need to take a long hard look at the values that are passed to the rAF callback - I don't think the numbers are consistent for each platform (I think some platforms get the time of the current frame, and others get the time of the frame that's being rendered/about to be displayed [which is more correct]... but I think all of them get the wrong time, in that they're not getting the time that the frame they're rendering will actually be displayed, but the time that it will be passed to the compositor.)
Flags: needinfo?(vladimir)
Blocks: 968240
Mason, Jeff said you will be able to help make a decision here.
Flags: needinfo?(mchang)
The problem here is that there is actual real jitter with vsync coming from hardware. On mac, it's usually pretty good at giving values that are flatline 16.66666666 ms apart. On Windows, depending on the hardware, timings can vary all the time with huge sway [1], thus producing jitter. Even on NVidia quadro class GPUs, the values we'd get from the DWM were generally +-1 ms from a perfect 16.6 ms interval. 

IIRC, what Chrome does is (last time I checked), it queries what the current vsync rate is, and basically has a software timer and just says "the vysnc timestamp is exactly the rate from the last one", so it seems perfect, but it wasn't listening to actual vsync on every interval. I guess we could change it to copy chrome, but I feel bad doing that. 

[1] https://dxr.mozilla.org/mozilla-central/source/gfx/thebes/gfxWindowsPlatform.cpp?from=gfxWindowsPlatform.cpp#2817
Flags: needinfo?(mchang)
Whiteboard: gfx-noted
(In reply to Mason Chang [:mchang] from comment #12)

I think the value in the specification for requestAnimationFrame might be of very little use. When producing an animation you don't care about the current time so much as you care about the presentation time for the frame that you're producing. i.e. if the monitor is running at 60Hz and you can produce frames that quickly the timestamps should be 16.6 ms apart regardless of what qpcVBlank actually says.

If we're getting qpcVBlank values that very by +-1ms I question whether they actually represent the actual timings of the monitor.
Another test case using performance.now() as measure, vs the timer given to rFA: http://codepen.io/digitarald/pen/KMVKgW

Chrome in this test has the same jitter as Firefox on OSX: https://www.dropbox.com/s/7h228be5cahonhb/Screenshot%202016-06-08%2014.50.55.PNG?dl=0
(In reply to Jeff Muizelaar [:jrmuizel] from comment #13)
> (In reply to Mason Chang [:mchang] from comment #12)
> 
> I think the value in the specification for requestAnimationFrame might be of
> very little use. When producing an animation you don't care about the
> current time so much as you care about the presentation time for the frame
> that you're producing. i.e. if the monitor is running at 60Hz and you can
> produce frames that quickly the timestamps should be 16.6 ms apart
> regardless of what qpcVBlank actually says.
> 
> If we're getting qpcVBlank values that very by +-1ms I question whether they
> actually represent the actual timings of the monitor.

Well technically it's the DWMComposition time, not VBlank in our implementation since we can't use WaitForVBlank. It also depends heavily on the GPU with nvidia gpus generally being better but intel gpus can have very big sways.
From what we discussed this morning, we listen to vblank and report a constant interval after vblank wakes us up. Software vsync already mostly tries to give a constant vsync interval. On OS X, hardware already gives us pretty nice intervals.
Attachment #8761832 - Flags: review?(jmuizelaar)
Also, from the test case, we still have some jitter with rAF due to other factors. For example, if the refresh driver takes too long to paint, we'll have missed an interval and get some jitter. This still happens for me sometimes with the attached test case.
platform-rel: --- → ?
Whiteboard: gfx-noted → gfx-noted [platform-rel-jQuery]
Are we doing something here, Mason?
Flags: needinfo?(mchang)
(In reply to Andrew Overholt [:overholt] from comment #18)
> Are we doing something here, Mason?

Still waiting on review.
Flags: needinfo?(mchang) → needinfo?(jmuizelaar)
Comment on attachment 8761832 [details] [diff] [review]
Use constant vsync interval to report to NotifyVsync observers

Review of attachment 8761832 [details] [diff] [review]:
-----------------------------------------------------------------

Sorry this took so long.
Attachment #8761832 - Flags: review?(jmuizelaar) → review+
Assignee: nobody → mchang
Flags: needinfo?(jmuizelaar)
Sorry still trying to figure out the try failures here - https://treeherder.mozilla.org/#/jobs?repo=try&revision=474f441c2e56
Try success - https://treeherder.mozilla.org/#/jobs?repo=try&revision=ccbc06a5ff65

Looks like having too many warnings in a log makes all the tests fail.
Pushed by mchang@mozilla.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/445065352cf0
Report a constant vsync interval diff on windows. r=jrmuizel
https://hg.mozilla.org/mozilla-central/rev/445065352cf0
Status: NEW → RESOLVED
Closed: 8 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla50
Blocks: 1315230
https://bugzilla.mozilla.org/show_bug.cgi?id=1412629

I see a lot of the screen breaks!
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: