diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Visuals/Rendering/RenderLoop.cs index 789d028a3a..b2a6b0da19 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLoop.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLoop.cs @@ -18,6 +18,7 @@ namespace Avalonia.Rendering { private readonly IDispatcher _dispatcher; private List _items = new List(); + private List _itemsCopy = new List(); private IRenderTimer _timer; private int _inTick; private int _inUpdate; @@ -63,11 +64,14 @@ namespace Avalonia.Rendering Contract.Requires(i != null); Dispatcher.UIThread.VerifyAccess(); - _items.Add(i); - - if (_items.Count == 1) + lock (_items) { - Timer.Tick += TimerTick; + _items.Add(i); + + if (_items.Count == 1) + { + Timer.Tick += TimerTick; + } } } @@ -76,12 +80,14 @@ namespace Avalonia.Rendering { Contract.Requires(i != null); Dispatcher.UIThread.VerifyAccess(); - - _items.Remove(i); - - if (_items.Count == 0) + lock (_items) { - Timer.Tick -= TimerTick; + _items.Remove(i); + + if (_items.Count == 0) + { + Timer.Tick -= TimerTick; + } } } @@ -129,10 +135,20 @@ namespace Avalonia.Rendering }, DispatcherPriority.Render); } - for(int i = 0; i < _items.Count; i++) + lock (_items) + { + _itemsCopy.Clear(); + foreach (var i in _items) + _itemsCopy.Add(i); + } + + for (int i = 0; i < _itemsCopy.Count; i++) { - _items[i].Render(); + _itemsCopy[i].Render(); } + + _itemsCopy.Clear(); + } catch (Exception ex) { diff --git a/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs b/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs new file mode 100644 index 0000000000..79e029ccd2 --- /dev/null +++ b/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs @@ -0,0 +1,72 @@ +using System; +using System.Diagnostics; +using System.Threading; + +namespace Avalonia.Rendering +{ + public class SleepLoopRenderTimer : IRenderTimer + { + private Action _tick; + private int _count; + private readonly object _lock = new object(); + private bool _running; + private readonly Stopwatch _st = Stopwatch.StartNew(); + private readonly TimeSpan _timeBetweenTicks; + + public SleepLoopRenderTimer(int fps) + { + _timeBetweenTicks = TimeSpan.FromSeconds(1d / fps); + } + + public event Action Tick + { + add + { + lock (_lock) + { + _tick += value; + _count++; + if (_running) + return; + _running = true; + new Thread(LoopProc) { IsBackground = true }.Start(); + } + + } + remove + { + lock (_lock) + { + _tick -= value; + _count--; + } + } + } + + void LoopProc() + { + var now = _st.Elapsed; + var lastTick = now; + + while (true) + { + var timeTillNextTick = lastTick + _timeBetweenTicks - now; + if (timeTillNextTick.TotalMilliseconds > 1) Thread.Sleep(timeTillNextTick); + + lock (_lock) + { + if (_count == 0) + { + _running = false; + return; + } + } + + _tick?.Invoke(now); + now = _st.Elapsed; + } + } + + + } +} diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 9e9c4fd30e..7f3255d4da 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -47,7 +47,7 @@ namespace Avalonia.X11 AvaloniaLocator.CurrentMutable.BindToSelf(this) .Bind().ToConstant(this) .Bind().ToConstant(new X11PlatformThreading(this)) - .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(new SleepLoopRenderTimer(60)) .Bind().ToConstant(new RenderLoop()) .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Control)) .Bind().ToFunc(() => KeyboardDevice)