From 133dee2a6cacf895470ae75bccd617809345b1eb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 26 Mar 2024 14:57:17 +0100 Subject: [PATCH 1/3] Use 0.2 seconds for input starvation cutoff. 1 second was too long when running on resource-constrained devices. --- src/Avalonia.Base/Media/MediaContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/MediaContext.cs b/src/Avalonia.Base/Media/MediaContext.cs index a21e62d7467..e6848ac4c5b 100644 --- a/src/Avalonia.Base/Media/MediaContext.cs +++ b/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 const double MaxSecondsWithoutInput = 0.2; private readonly Action _render; private readonly Action _inputMarkerHandler; private readonly HashSet _requestedCommits = new(); From c8a55c89eb16ab15117a1c227ba6469decca8839 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 26 Mar 2024 14:58:07 +0100 Subject: [PATCH 2/3] Allow input starvation code to run. Using `ScheduleRender(true)` here prevented the input starvation code from running. --- src/Avalonia.Base/Media/MediaContext.Compositor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/MediaContext.Compositor.cs b/src/Avalonia.Base/Media/MediaContext.Compositor.cs index 0d6aec9ae28..ff8c2b090ef 100644 --- a/src/Avalonia.Base/Media/MediaContext.Compositor.cs +++ b/src/Avalonia.Base/Media/MediaContext.Compositor.cs @@ -146,6 +146,6 @@ void ICompositorScheduler.CommitRequested(Compositor compositor) return; // TODO: maybe skip the full render here? - ScheduleRender(true); + ScheduleRender(false); } } From 0e92fe544a500b562b058d4542836abf2042dffe Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 28 Mar 2024 09:58:10 +0100 Subject: [PATCH 3/3] Add DispatcherOptions. And allow setting the input starvation timeout there. --- src/Avalonia.Base/Media/MediaContext.cs | 13 +++++++++--- .../Threading/DispatcherOptions.cs | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 src/Avalonia.Base/Threading/DispatcherOptions.cs diff --git a/src/Avalonia.Base/Media/MediaContext.cs b/src/Avalonia.Base/Media/MediaContext.cs index e6848ac4c5b..9747d01c7ea 100644 --- a/src/Avalonia.Base/Media/MediaContext.cs +++ b/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 = 0.2; + private readonly double MaxSecondsWithoutInput; private readonly Action _render; private readonly Action _inputMarkerHandler; private readonly HashSet _requestedCommits = new(); @@ -37,12 +37,13 @@ private record TopLevelInfo(Compositor Compositor, CompositingRenderer Renderer private readonly Dictionary _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 @@ public static MediaContext Instance // 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(); + if (context == null) - AvaloniaLocator.CurrentMutable.Bind().ToConstant(context = new(Dispatcher.UIThread)); + { + var opts = AvaloniaLocator.Current.GetService() ?? new(); + context = new MediaContext(Dispatcher.UIThread, opts.InputStarvationTimeout); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(context); + } + return context; } } diff --git a/src/Avalonia.Base/Threading/DispatcherOptions.cs b/src/Avalonia.Base/Threading/DispatcherOptions.cs new file mode 100644 index 00000000000..e12497a5982 --- /dev/null +++ b/src/Avalonia.Base/Threading/DispatcherOptions.cs @@ -0,0 +1,21 @@ +using System; + +namespace Avalonia.Threading; + +/// +/// AppBuilder options for configuring the . +/// +public class DispatcherOptions +{ + /// + /// Gets or sets a timeout after which the dispatcher will start prioritizing input events over + /// rendering. The default value is 1 second. + /// + /// + /// 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. + /// + public TimeSpan InputStarvationTimeout { get; set; } = TimeSpan.FromSeconds(1); +}