diff --git a/src/Avalonia.Base/Rendering/ThreadProxyRenderTimer.cs b/src/Avalonia.Base/Rendering/ThreadProxyRenderTimer.cs new file mode 100644 index 0000000000..0f3387cd1a --- /dev/null +++ b/src/Avalonia.Base/Rendering/ThreadProxyRenderTimer.cs @@ -0,0 +1,70 @@ +using System; +using System.Diagnostics; +using System.Threading; +using Avalonia.Metadata; + +namespace Avalonia.Rendering; + +[PrivateApi] +public sealed class ThreadProxyRenderTimer : IRenderTimer +{ + private readonly IRenderTimer _inner; + private readonly Stopwatch _stopwatch; + private readonly Thread _timerThread; + private readonly AutoResetEvent _autoResetEvent; + private Action? _tick; + private int _subscriberCount; + private bool _registered; + + public ThreadProxyRenderTimer(IRenderTimer inner, int maxStackSize = 1 * 1024 * 1024) + { + _inner = inner; + _stopwatch = new Stopwatch(); + _autoResetEvent = new AutoResetEvent(false); + _timerThread = new Thread(RenderTimerThreadFunc, maxStackSize) { Name = "RenderTimerLoop", IsBackground = true }; + } + + public event Action Tick + { + add + { + _tick += value; + + if (!_registered) + { + _registered = true; + _timerThread.Start(); + } + + if (_subscriberCount++ == 0) + { + _inner.Tick += InnerTick; + } + } + + remove + { + if (--_subscriberCount == 0) + { + _inner.Tick -= InnerTick; + } + + _tick -= value; + } + } + + private void RenderTimerThreadFunc() + { + while (_autoResetEvent.WaitOne()) + { + _tick?.Invoke(_stopwatch.Elapsed); + } + } + + private void InnerTick(TimeSpan obj) + { + _autoResetEvent.Set(); + } + + public bool RunsInBackground => true; +} diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index f24c6fa96f..68da780dda 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -108,7 +108,7 @@ namespace Avalonia.Native .Bind().ToConstant(new NativePlatformSettings(_factory.CreatePlatformSettings())) .Bind().ToConstant(this) .Bind().ToConstant(new ClipboardImpl(_factory.CreateClipboard())) - .Bind().ToConstant(new AvaloniaNativeRenderTimer(_factory.CreatePlatformRenderTimer())) + .Bind().ToConstant(new ThreadProxyRenderTimer(new AvaloniaNativeRenderTimer(_factory.CreatePlatformRenderTimer()))) .Bind().ToConstant(new MacOSMountedVolumeInfoProvider()) .Bind().ToConstant(new AvaloniaNativeDragSource(_factory)) .Bind().ToConstant(applicationPlatform)