Browse Source

Now using non-pumping mode for UpdateScene in deferred renderer, also improved the non-pumping sync context

Nikita Tsukanov 5 years ago
parent
commit
6e3bf60f28

+ 14 - 0
src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace Avalonia.Utilities
+{
+    public class NonPumpingLockHelper
+    {
+        public interface IHelperImpl
+        {
+            IDisposable Use();
+        }
+
+        public static IDisposable Use() => AvaloniaLocator.Current.GetService<IHelperImpl>()?.Use();
+    }
+}

+ 1 - 0
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -607,6 +607,7 @@ namespace Avalonia.Rendering
         private bool? UpdateScene()
         {
             Dispatcher.UIThread.VerifyAccess();
+            using var noPump = NonPumpingLockHelper.Use();
             lock (_sceneLock)
             {
                 if (_disposed)

+ 22 - 4
src/Windows/Avalonia.Win32/NonPumpingSyncContext.cs

@@ -2,6 +2,7 @@ using System;
 using System.Runtime.ConstrainedExecution;
 using System.Threading;
 using Avalonia.Threading;
+using Avalonia.Utilities;
 using Avalonia.Win32.Interop;
 
 namespace Avalonia.Win32
@@ -10,9 +11,9 @@ namespace Avalonia.Win32
     {
         private readonly SynchronizationContext _inner;
 
-        private NonPumpingSyncContext()
+        private NonPumpingSyncContext(SynchronizationContext inner)
         {
-            _inner = Current;
+            _inner = inner;
             SetWaitNotificationRequired();
             SetSynchronizationContext(this);
         }
@@ -27,8 +28,25 @@ namespace Avalonia.Win32
                 millisecondsTimeout, false);
         }
 
-        public void Dispose() => SynchronizationContext.SetSynchronizationContext(_inner);
+        public void Dispose() => SetSynchronizationContext(_inner);
 
-        public static IDisposable Use() => new NonPumpingSyncContext();
+        public static IDisposable Use()
+        {
+            var current = Current;
+            if (current == null)
+            {
+                if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
+                    return null;
+            }
+            if (current is NonPumpingSyncContext)
+                return null;
+            
+            return new NonPumpingSyncContext(current);
+        }
+
+        internal class HelperImpl : NonPumpingLockHelper.IHelperImpl
+        {
+            IDisposable NonPumpingLockHelper.IHelperImpl.Use() => NonPumpingSyncContext.Use();
+        }
     }
 }

+ 2 - 0
src/Windows/Avalonia.Win32/Win32Platform.cs

@@ -16,6 +16,7 @@ using Avalonia.OpenGL.Egl;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Threading;
+using Avalonia.Utilities;
 using Avalonia.Win32.Input;
 using Avalonia.Win32.Interop;
 using static Avalonia.Win32.Interop.UnmanagedMethods;
@@ -110,6 +111,7 @@ namespace Avalonia.Win32
                 .Bind<IWindowingPlatform>().ToConstant(s_instance)
                 .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
                 .Bind<IPlatformIconLoader>().ToConstant(s_instance)
+                .Bind<NonPumpingLockHelper.IHelperImpl>().ToConstant(new NonPumpingSyncContext.HelperImpl())
                 .Bind<IMountedVolumeInfoProvider>().ToConstant(new WindowsMountedVolumeInfoProvider());
 
             Win32GlManager.Initialize();