Jelajahi Sumber

Fix input starvation issues on resource-constrained devices. (#15137)

* Use 0.2 seconds for input starvation cutoff.

1 second was too long when running on resource-constrained devices.

* Allow input starvation code to run.

Using `ScheduleRender(true)` here prevented the input starvation code from running.

* Add DispatcherOptions.

And allow setting the input starvation timeout there.
Steven Kirk 1 tahun lalu
induk
melakukan
8a65cb6a68

+ 1 - 1
src/Avalonia.Base/Media/MediaContext.Compositor.cs

@@ -146,6 +146,6 @@ partial class MediaContext
             return;
 
         // TODO: maybe skip the full render here?
-        ScheduleRender(true);
+        ScheduleRender(false);
     }
 }

+ 10 - 3
src/Avalonia.Base/Media/MediaContext.cs

@@ -16,7 +16,7 @@ internal partial class MediaContext : ICompositorScheduler
     private TimeSpan _inputMarkerAddedAt;
     private bool _isRendering;
     private bool _animationsAreWaitingForComposition;
-    private const double MaxSecondsWithoutInput = 1;
+    private readonly double MaxSecondsWithoutInput;
     private readonly Action _render;
     private readonly Action _inputMarkerHandler;
     private readonly HashSet<Compositor> _requestedCommits = new();
@@ -37,12 +37,13 @@ internal partial class MediaContext : ICompositorScheduler
 
     private readonly Dictionary<object, TopLevelInfo> _topLevels = new();
 
-    private MediaContext(Dispatcher dispatcher)
+    private MediaContext(Dispatcher dispatcher, TimeSpan inputStarvationTimeout)
     {
         _render = Render;
         _inputMarkerHandler = InputMarkerHandler;
         _clock = new(this);
         _dispatcher = dispatcher;
+        MaxSecondsWithoutInput = inputStarvationTimeout.TotalSeconds;
         _animationsTimer.Tick += (_, _) =>
         {
             _animationsTimer.Stop();
@@ -57,8 +58,14 @@ internal partial class MediaContext : ICompositorScheduler
             // Technically it's supposed to be a thread-static singleton, but we don't have multiple threads
             // and need to do a full reset for unit tests
             var context = AvaloniaLocator.Current.GetService<MediaContext>();
+
             if (context == null)
-                AvaloniaLocator.CurrentMutable.Bind<MediaContext>().ToConstant(context = new(Dispatcher.UIThread));
+            {
+                var opts = AvaloniaLocator.Current.GetService<DispatcherOptions>() ?? new();
+                context = new MediaContext(Dispatcher.UIThread, opts.InputStarvationTimeout);
+                AvaloniaLocator.CurrentMutable.Bind<MediaContext>().ToConstant(context);
+            }
+
             return context;
         }
     }

+ 21 - 0
src/Avalonia.Base/Threading/DispatcherOptions.cs

@@ -0,0 +1,21 @@
+using System;
+
+namespace Avalonia.Threading;
+
+/// <summary>
+/// AppBuilder options for configuring the <see cref="Dispatcher"/>.
+/// </summary>
+public class DispatcherOptions
+{
+    /// <summary>
+    /// Gets or sets a timeout after which the dispatcher will start prioritizing input events over
+    /// rendering. The default value is 1 second.
+    /// </summary>
+    /// <remarks>
+    /// If no input events are processed within this time, the dispatcher will start prioritizing
+    /// input events over rendering to prevent the application from becoming unresponsive. This may
+    /// need to be lowered on resource-constrained platforms where input events are processed on
+    /// the same thread as rendering.
+    /// </remarks>
+    public TimeSpan InputStarvationTimeout { get; set; } = TimeSpan.FromSeconds(1);
+}