From 9debf63c71fc7b17e4c08b914fc977f67f0d5162 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 29 May 2023 20:31:10 +0600 Subject: [PATCH] Fixed ToggleSwitch animation --- .../Media/MediaContext.Compositor.cs | 13 ++++++++--- src/Avalonia.Base/Media/MediaContext.cs | 23 ++++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Base/Media/MediaContext.Compositor.cs b/src/Avalonia.Base/Media/MediaContext.Compositor.cs index d9d3c915d4..6234f36a12 100644 --- a/src/Avalonia.Base/Media/MediaContext.Compositor.cs +++ b/src/Avalonia.Base/Media/MediaContext.Compositor.cs @@ -40,10 +40,10 @@ partial class MediaContext _animationsAreWaitingForComposition = false; // Check if we have uncommited changes - if (_scheduleCommitOnLastCompositionBatchCompletion && _pendingCompositionBatches.Count > 0) + if (_scheduleCommitOnLastCompositionBatchCompletion) { - CommitCompositorsWithThrottling(); _scheduleCommitOnLastCompositionBatchCompletion = false; + CommitCompositorsWithThrottling(); } // Check if there are active animations and schedule the next render else if(_clock.HasSubscriptions) @@ -52,16 +52,23 @@ partial class MediaContext } + /// + /// Triggers a composition commit if any batches are waiting to be sent, + /// handles throttling + /// + /// true if there are pending commits in-flight and there will be a "all-done" callback later private bool CommitCompositorsWithThrottling() { // Check if we are still waiting for previous composition batches if (_pendingCompositionBatches.Count > 0) { _scheduleCommitOnLastCompositionBatchCompletion = true; + // Previous commit isn't handled yet return true; } - + if (_requestedCommits.Count == 0) + // Nothing to do, and there are no pending commits return false; foreach (var c in _requestedCommits) diff --git a/src/Avalonia.Base/Media/MediaContext.cs b/src/Avalonia.Base/Media/MediaContext.cs index 023b2f728c..c261dd80b8 100644 --- a/src/Avalonia.Base/Media/MediaContext.cs +++ b/src/Avalonia.Base/Media/MediaContext.cs @@ -28,6 +28,14 @@ internal partial class MediaContext : ICompositorScheduler private List? _invokeOnRenderCallbacks; private readonly Stack> _invokeOnRenderCallbackListPool = new(); + private DispatcherTimer _animationsTimer = new(DispatcherPriority.Render) + { + // Since this timer is used to drive animations that didn't contribute to the previous frame at all + // We can safely use 16ms interval until we fix our animation system to actually report the next expected + // frame + Interval = TimeSpan.FromMilliseconds(16) + }; + private Dictionary _topLevels = new(); private MediaContext() @@ -35,6 +43,11 @@ internal partial class MediaContext : ICompositorScheduler _render = Render; _inputMarkerHandler = InputMarkerHandler; _clock = new(this); + _animationsTimer.Tick += (_, _) => + { + _animationsTimer.Stop(); + ScheduleRender(false); + }; } public static MediaContext Instance @@ -129,14 +142,12 @@ internal partial class MediaContext : ICompositorScheduler break; } - - // We are currently using compositor commit callbacks to drive animations - // Later we should use WPF's approach that asks the animation system for the next tick time - // and use some timer if the next animation frame is not needed to be sent to the compositor immediately + if (_requestedCommits.Count > 0 || _clock.HasSubscriptions) { - _animationsAreWaitingForComposition = true; - CommitCompositorsWithThrottling(); + _animationsAreWaitingForComposition = CommitCompositorsWithThrottling(); + if (!_animationsAreWaitingForComposition && _clock.HasSubscriptions) + _animationsTimer.Start(); } }