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

[UWP] Xamarin.Forms.Device.StartTimer is inaccurate #1623

Closed
charlesroddie opened this issue Jan 19, 2018 · 7 comments

Comments

Projects
None yet
2 participants
@charlesroddie
Copy link

commented Jan 19, 2018

Description

Xamarin.Forms.Device.StartTimer(interval...), at least on UWP, is linked to screen refresh rate, takes longer than interval to fire, and has a minimum time to fire of 2 frames.

This obviously makes it unusable as a method for displaying smooth graphics, as in the suggested code here which requres updating once a frame.

I have not tested on other Xamarin.Forms platforms.

Steps to Reproduce

FSharp test code:

let testTimer interval =
    let mutable n = 0
    let sw = Stopwatch();
    do  sw.Start()
    do  Xamarin.Forms.Device.StartTimer(TimeSpan.FromSeconds(interval), fun () ->
            n <- n+1
            let expectedElapsed = (float n) * interval
            let trueElapsed = sw.Elapsed.TotalSeconds
            if n % (max (int (1./interval)) 1) = 0 then
                Debug.WriteLine(sprintf "interval: %A true / expected: %A trueInterval: %A" interval (trueElapsed/expectedElapsed) (trueElapsed/float n))
            true)
do  [10.;1.;0.1;0.05;1./30.;1./40.;1./60.;1./120.;1./240.] |> List.iter testTimer

Wait for numbers to stabilize.

Results:

on a 59fps screen

intended interval actual/expected actual interval
10 1.002 10.02
1 1.017 1.017
0.1 1.17 0.117
0.05 1.34 0.067 (4 screen frame refreshes)
0.033.. 1.51 0.050 (3 frames)
0.025 2.01 0.050 (3 frames)
0.0166.. 3.01 0.050 (3 frames)
0.00833.. 4.02 0.034 (2 frames)
0.004166.. 8.05 0.034 (2 frames)

on a 30fps screen

intended interval actual/expected actual interval
10 1.006 10.06
1 1.066 1.066
0.1 1.68 0.168
0.05 2.01 0.10 (3 frames)
0.033.. 2.01 0.067 (2 frames)
0.025 2.68 0.067 (2 frames)
0.0166.. 4.03 0.067 (2 frames)
0.00833.. 8.05 0.034 (2 frames)
0.004166.. 16.09 0.067 (2 frames)

Basic Information

  • Version with issue: Xamarin Forms 2.5.0.121934
  • Last known good version: -
  • Platform Target Frameworks:
    • iOS: untested
    • Android: untested
    • UWP: 16299

@pauldipietro pauldipietro added this to New in Triage Jan 19, 2018

@charlesroddie

This comment has been minimized.

Copy link
Author

commented Jan 19, 2018

Based on previous informal testing, I believe this issue either does not exist or is less serious on Android and iOS.

It is possible this issue is linked to choppy animations on UWP.

@charlesroddie

This comment has been minimized.

Copy link
Author

commented Jan 20, 2018

Workaround: setting interval = TimeSpan.Zero is equivalent to 1 frame on Windows so can be used to match screen fps.

@charlesroddie

This comment has been minimized.

Copy link
Author

commented Jan 20, 2018

I have not been able to test System.Threading.Timer yet, but if that works correctly then this issue could be resolved by just depreciating Xamarin.Forms.Device.StartTimer, since System.Threading.Timer is in .Net Standard 2.0.

@StephaneDelcroix

This comment has been minimized.

Copy link
Member

commented Jan 22, 2018

All timers are inaccurate, and the interval is always the minimal guaranteed interval. But I agree that in this case, it's off by a wide margin for precise high-frequency timing.

deprecating it, in favor of System.Threading.Timer is probably the right thing to do...

@StephaneDelcroix StephaneDelcroix self-assigned this Jan 22, 2018

@StephaneDelcroix StephaneDelcroix moved this from New to Proposal in Triage Jan 22, 2018

@jassmith jassmith added this to Backlog in Enhancements Jan 22, 2018

@charlesroddie

This comment has been minimized.

Copy link
Author

commented Jan 24, 2018

The Xamarin Forms code looks OK:

public void StartTimer(TimeSpan interval, Func<bool> callback) {
var timer = new DispatcherTimer { Interval = interval };...

There are apparently threading advantages of DispatcherTimer comapred to System.Threading.Timer. The problem may be that thread scheduling has a granularity of 50ms . This suggests that the slowness of the timer is a Windows issue affecting all Timers rather than a Xamarin.Forms bug.

However, given that this slowness exists, Xamarin.Forms code which uses timers to get smooth animation should be changed.

A search of DispatcherTimer in this repo returns several examples with Interval=TimeSpan.FromMilliseconds(15) or 16. This is clearly an attempt to get 60fps but it will fail on Windows. LayoutPerformanceGallery.cs also uses a TimeSpan.FromMilliseconds(10).

A workaround appears to be to use TimeSpan.Zero, although this would be a nasty hack. A more proper way to get smooth animation would be to use a CompositionTarget.Rendering event (see comments in the link), although that is Windows-specific.

Would the best solution, to allow Xamarin.Forms users to do something once a frame, be to create a Xamarin.Forms Rendering event, assuming this does not already exist?

I hope this comment is helpful even though I have limited understanding of the underlying issues.

@mattleibow do you have an opinion about how to achieve smooth rendering on Windows? This affects SkiaSharp uses, including your answer in the link in the OP.

@charlesroddie

This comment has been minimized.

Copy link
Author

commented Feb 21, 2018

I propose splitting this into two issues:

  1. Use CompositionTarget.Rendering events on Windows in the relevant places in the repo. This should fix choppy animations that people complain about on UWP.
  2. Expose a Rendering event in Xamarin.Forms. This will allow users to draw things once a frame.
@charlesroddie

This comment has been minimized.

Copy link
Author

commented Feb 21, 2018

Created the two issues so @StephaneDelcroix you can close this issue, which was getting overcomplicated.

@samhouts samhouts removed this from Proposal in Triage Feb 22, 2018

Enhancements automation moved this from Backlog to Done Feb 27, 2018

@StephaneDelcroix StephaneDelcroix moved this from Done to Rejected in Enhancements Feb 27, 2018

@samhouts samhouts removed this from Rejected in Enhancements Jun 12, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.