Browse Source

Use single CFDisplayLink for everything

Nikita Tsukanov 10 years ago
parent
commit
40b3765916

+ 8 - 1
src/Skia/Perspex.Skia.iOS/SkiaView.cs

@@ -1,5 +1,7 @@
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
 using System.Runtime.InteropServices;
 using System.Text;
 using CoreAnimation;
@@ -31,11 +33,16 @@ namespace Perspex.Skia.iOS
         }
 
 
+        protected SkiaView(Action<Action> registerFrame) : base(UIScreen.MainScreen.ApplicationFrame, GetContext())
+        {
+            registerFrame(OnFrame);
+        }
+
         protected SkiaView() : base(UIScreen.MainScreen.ApplicationFrame, GetContext())
         {
             (_link = CADisplayLink.Create(() => OnFrame())).AddToRunLoop(NSRunLoop.Main, NSRunLoop.NSDefaultRunLoopMode);
         }
-        
+
         protected void OnFrame()
         {
             if (_drawQueued)

+ 1 - 1
src/iOS/Perspex.iOS/PerspexAppDelegate.cs

@@ -41,7 +41,7 @@ namespace Perspex.iOS
                     .Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
                     .Bind<IMouseDevice>().ToConstant(MouseDevice)
                     .Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
-                    .Bind<IPlatformThreadingInterface>().ToConstant(new PlatformThreadingInterface())
+                    .Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
                     .Bind<IWindowImpl>().ToConstant(controller.PerspexView);
                 SkiaPlatform.Initialize();
             });

+ 1 - 1
src/iOS/Perspex.iOS/PerspexView.cs

@@ -23,7 +23,7 @@ namespace Perspex.iOS
         private readonly UIViewController _controller;
         private IInputRoot _inputRoot;
 
-        public PerspexView(UIWindow window, UIViewController controller)
+        public PerspexView(UIWindow window, UIViewController controller) : base(onFrame => PlatformThreadingInterface.Instance.Render = onFrame)
         {
             if (controller == null) throw new ArgumentNullException(nameof(controller));
             _window = window;

+ 61 - 13
src/iOS/Perspex.iOS/PlatformThreadingInterface.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Linq;
 using System.Reactive.Disposables;
 using System.Text;
@@ -13,26 +14,63 @@ namespace Perspex.iOS
 {
     class PlatformThreadingInterface :  IPlatformThreadingInterface
     {
-        readonly List<Action> _timers = new List<Action>();
+        static Stopwatch St = Stopwatch.StartNew();
+        class Timer
+        {
+            readonly Action _tick;
+            readonly TimeSpan _interval;
+            TimeSpan _nextTick;
+
+            public Timer(Action tick, TimeSpan interval)
+            {
+                _tick = tick;
+                _interval = interval;
+                _nextTick = St.Elapsed + _interval;
+            }
+
+            public void Tick(TimeSpan now)
+            {
+                if (now > _nextTick)
+                {
+                    _nextTick = now + _interval;
+                    _tick();
+                }
+            }
+        }
+
+        readonly List<Timer> _timers = new List<Timer>();
         bool _signaled;
+        readonly object _lock = new object();
+        private CADisplayLink _link;
+        public Action Render { get; set; }
 
-        public PlatformThreadingInterface()
+        PlatformThreadingInterface()
         {
-            CADisplayLink.Create(OnFrame).AddToRunLoop(NSRunLoop.Main, NSRunLoop.NSDefaultRunLoopMode);
+            // For some reason it doesn't work when I specify OnFrame method directly
+            // ReSharper disable once ConvertClosureToMethodGroup
+            (_link = CADisplayLink.Create(() => OnFrame())).AddToRunLoop(NSRunLoop.Main, NSRunLoop.NSDefaultRunLoopMode);
         }
 
         private void OnFrame()
         {
-            foreach (var timer in _timers.ToList())
-            {
-                timer();
-            }
+            var now = St.Elapsed;
+            List<Timer> timers;
+            lock (_lock)
+                timers = _timers.ToList();
+
+            foreach (var timer in timers)
+                timer.Tick(now);
 
-            if (_signaled)
+            do
             {
-                _signaled = false;
+                lock (_lock)
+                    if (!_signaled)
+                        break;
+                    else
+                        _signaled = false;
                 Signaled?.Invoke();
-            }
+            } while (false);
+            Render?.Invoke();
         }
 
         public void RunLoop(CancellationToken cancellationToken)
@@ -41,16 +79,26 @@ namespace Perspex.iOS
 
         public IDisposable StartTimer(TimeSpan interval, Action tick)
         {
-            _timers.Add(tick);
-            return Disposable.Create(() => _timers.Remove(tick));
+            lock (_lock)
+            {
+                var timer = new Timer(tick, interval);
+                _timers.Add(timer);
+                return Disposable.Create(() =>
+                {
+                    lock (_lock) _timers.Remove(timer);
+                });
+            }
         }
 
         public void Signal()
         {
-            _signaled = true;
+            lock (_lock)
+                _signaled = true;
         }
 
         public bool CurrentThreadIsLoopThread => NSThread.Current.IsMainThread;
+        public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface();
+
         public event Action Signaled;
     }
 }