Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new AVP::AnimationOptimization property #6269

Merged
merged 12 commits into from
Jan 25, 2022
Merged
112 changes: 108 additions & 4 deletions dev/AnimatedVisualPlayer/AnimatedVisualPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ void AnimatedVisualPlayer::AnimationPlay::Start()
MUX_ASSERT(m_owner);
MUX_ASSERT(!m_controller);

// Set lastPlayProgress to fromProgress.
// When we call Stop() root progress will be set to lastPlayProgress.
m_owner->m_lastPlayProgress = m_fromProgress;

// If the duration is really short (< 20ms) don't bother trying to animate.
if (m_playDuration < winrt::TimeSpan{ 20ms })
{
Expand Down Expand Up @@ -117,6 +121,18 @@ void AnimatedVisualPlayer::AnimationPlay::Start()
// Subscribe to the batch completed event.
m_batchCompletedToken = m_batch.Completed([this](winrt::IInspectable const&, winrt::CompositionBatchCompletedEventArgs const&)
{
if (m_owner)
{
// If currentPlay completed succesfully, set lastPlayProgress to toProgress.
m_owner->m_lastPlayProgress = m_toProgress;

// If cache mode is set to None - destroy animations immediately after player stops.
if (m_owner->AnimationsCacheMode() == winrt::AnimationsCacheModeEnum::None)
{
m_owner->DestroyAnimations();
}
}

// Complete the play when the batch completes.
//
// The "this" pointer is guaranteed to be valid because:
Expand Down Expand Up @@ -622,12 +638,20 @@ winrt::IAsyncAction AnimatedVisualPlayer::PlayAsync(double fromProgress, double
co_return;
}

// Make sure that animations are instantiated.
InstantiateAnimations();

// Used to detect reentrance.
const auto version = ++m_playAsyncVersion;

// Cause any other plays to return.
// Complete m_nowPlaying if it is still running.
// Identical to Stop() call but without destroying the animations.
// WARNING - this call may cause reentrance via the IsPlaying DP.
Stop();
if (m_nowPlaying)
{
m_progressPropertySet.InsertScalar(L"Progress", static_cast<float>(m_lastPlayProgress));
m_nowPlaying->Complete();
}

if (version != m_playAsyncVersion)
{
Expand Down Expand Up @@ -707,13 +731,17 @@ void AnimatedVisualPlayer::SetProgress(double progress)
return;
}

// Make sure that animations are instantiated.
InstantiateAnimations();

auto clampedProgress = std::clamp(static_cast<float>(progress), 0.0F, 1.0F);

// WARNING: Reentrance via IsPlaying DP may occur from this point down to the end of the method
// iff m_nowPlaying.

// Setting the Progress value will stop the current play.
m_progressPropertySet.InsertScalar(L"Progress", static_cast<float>(clampedProgress));
m_lastPlayProgress = progress;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_lastPlayProgress = progress

m_lastPlayProgress will be clamped later when it is used? (i.e. in stop?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, probably it is better to set it to clampedProgress here, thx


// Ensure the current PlayAsync task is completed.
// Note that this explicit call is necessary, even though InsertScalar
Expand All @@ -725,6 +753,11 @@ void AnimatedVisualPlayer::SetProgress(double progress)
{
m_nowPlaying->Complete();
}

// If cache mode is set to None - destroy annimations immediately.
if (AnimationsCacheMode() == winrt::AnimationsCacheModeEnum::None) {
DestroyAnimations();
}
}

// Public API.
Expand All @@ -736,7 +769,7 @@ void AnimatedVisualPlayer::Stop()
// Stop the animation by setting the Progress value to the fromProgress of the
// most recent play.
// This may cause reentrance via the IsPlaying DP.
SetProgress(m_currentPlayFromProgress);
SetProgress(m_lastPlayProgress);
}
}

Expand All @@ -755,6 +788,66 @@ void AnimatedVisualPlayer::OnAutoPlayPropertyChanged(
}
}

void AnimatedVisualPlayer::OnAnimationsCacheModePropertyChanged(
winrt::DependencyPropertyChangedEventArgs const& args)
{
auto cacheMode = unbox_value<winrt::AnimationsCacheModeEnum>(args.NewValue());

if (m_nowPlaying)
{
// If there is something in play right now we should not instantiate/destroy animations.
return;
}

if (cacheMode == winrt::AnimationsCacheModeEnum::None)
{
DestroyAnimations();
}
else if (cacheMode == winrt::AnimationsCacheModeEnum::Always)
{
InstantiateAnimations();
}
}

void AnimatedVisualPlayer::InstantiateAnimations() {
if (m_isAnimationsCached)
{
return;
}

// Check if current animated visual supports instantiating animations and create them.
if (const auto& animatedVisual = m_animatedVisual.get())
{
if (const auto& animatedVisual2 = m_animatedVisual.try_as<winrt::IAnimatedVisual2>())
{
animatedVisual2.InstantiateAnimations(m_lastPlayProgress);
m_isAnimationsCached = true;
}
}
}

void AnimatedVisualPlayer::DestroyAnimations() {
if (!m_isAnimationsCached)
{
return;
}

// Call RequestCommit to make sure that previous compositor calls complete before destroying animations.
m_rootVisual.Compositor().RequestCommitAsync().Completed(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestCommitAsync() is only available on Rs4+ we need to gracefully handle the case where this API isn't available on RS2 and RS3.

[&](auto, auto) {
// Check if current animated visual supports destroyig animations.
if (const auto& animatedVisual = m_animatedVisual.get())
{
if (const auto& animatedVisual2 = m_animatedVisual.try_as<winrt::IAnimatedVisual2>())
{
animatedVisual2.DestroyAnimations();
m_isAnimationsCached = false;
}
}
}
);
}

void AnimatedVisualPlayer::OnFallbackContentPropertyChanged(
winrt::DependencyPropertyChangedEventArgs const& args)
{
Expand Down Expand Up @@ -845,7 +938,18 @@ void AnimatedVisualPlayer::UpdateContent()
}

winrt::IInspectable diagnostics{};
auto animatedVisual = source.TryCreateAnimatedVisual(m_rootVisual.Compositor(), diagnostics);
winrt::IAnimatedVisual animatedVisual;
if (auto source3 = source.try_as<winrt::IAnimatedVisualSource3>())
{
bool instantiateAnimations = AnimationsCacheMode() == winrt::AnimationsCacheModeEnum::Always;
animatedVisual = source3.TryCreateAnimatedVisual(m_rootVisual.Compositor(), instantiateAnimations, diagnostics);
m_isAnimationsCached = instantiateAnimations;
}
else
{
animatedVisual = source.TryCreateAnimatedVisual(m_rootVisual.Compositor(), diagnostics);
m_isAnimationsCached = true;
}
m_animatedVisual.set(animatedVisual);

if (!animatedVisual)
Expand Down
9 changes: 8 additions & 1 deletion dev/AnimatedVisualPlayer/AnimatedVisualPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ struct AnimatedVisualPlayer:

void OnAutoPlayPropertyChanged(winrt::DependencyPropertyChangedEventArgs const& args);

void OnAnimationsCacheModePropertyChanged(winrt::DependencyPropertyChangedEventArgs const& args);

void OnFallbackContentPropertyChanged(winrt::DependencyPropertyChangedEventArgs const& args);

void OnPlaybackRatePropertyChanged(winrt::DependencyPropertyChangedEventArgs const& args);
Expand All @@ -104,6 +106,9 @@ struct AnimatedVisualPlayer:
void OnHiding();
void OnUnhiding();

void InstantiateAnimations();
void DestroyAnimations();

//
// Initialized by the constructor.
//
Expand All @@ -127,7 +132,7 @@ struct AnimatedVisualPlayer:
winrt::float2 m_animatedVisualSize;
winrt::Composition::Visual m_animatedVisualRoot{ nullptr };
int m_playAsyncVersion{ 0 };
double m_currentPlayFromProgress{ 0 };
double m_lastPlayProgress{ 0 };
// The play that will be stopped when Stop() is called.
std::shared_ptr<AnimationPlay> m_nowPlaying{ nullptr };
winrt::IDynamicAnimatedVisualSource::AnimatedVisualInvalidated_revoker m_dynamicAnimatedVisualInvalidatedRevoker{};
Expand All @@ -141,4 +146,6 @@ struct AnimatedVisualPlayer:
// This is used to differentiate the first Loaded event (when the element has never been
// unloaded) from later Loaded events.
bool m_isUnloaded{ false };

bool m_isAnimationsCached{ false };
};
30 changes: 30 additions & 0 deletions dev/AnimatedVisualPlayer/AnimatedVisualPlayer.idl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ interface IAnimatedVisual
Windows.Foundation.TimeSpan Duration{ get; };
};

[MUX_PREVIEW]
[webhosthidden]
interface IAnimatedVisual2 requires IAnimatedVisual
{
void InstantiateAnimations(Double progressHint);
void DestroyAnimations();
};

[MUX_PUBLIC]
[webhosthidden]
interface IAnimatedVisualSource
Expand All @@ -28,6 +36,16 @@ interface IDynamicAnimatedVisualSource
event Windows.Foundation.TypedEventHandler<IDynamicAnimatedVisualSource, Object> AnimatedVisualInvalidated;
};

[MUX_PREVIEW]
[webhosthidden]
interface IAnimatedVisualSource3
{
IAnimatedVisual2 TryCreateAnimatedVisual(
Windows.UI.Composition.Compositor compositor,
Boolean instantiateAnimations,
out Object diagnostics);
};

[MUX_INTERNAL]
[webhosthidden]
interface ISelfPlayingAnimatedVisual
Expand All @@ -43,6 +61,14 @@ interface ISelfPlayingAnimatedVisual
void SetSize(Windows.Foundation.Size size);
};

[MUX_PUBLIC]
[webhosthidden]
enum AnimationsCacheModeEnum
{
None,
Always
};

[MUX_PUBLIC]
[webhosthidden]
[contentproperty("Source")]
Expand All @@ -62,6 +88,9 @@ unsealed runtimeclass AnimatedVisualPlayer
[MUX_DEFAULT_VALUE("true")]
[MUX_PROPERTY_CHANGED_CALLBACK(TRUE)]
Boolean AutoPlay;
[MUX_DEFAULT_VALUE("winrt::AnimationsCacheModeEnum::Always")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You would need to version these APIs since you are adding to an existing type. See MUX_PUBLIC_V2 to see how it is done in other types. @kmahone as FYI

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, done

[MUX_PROPERTY_CHANGED_CALLBACK(TRUE)]
AnimationsCacheModeEnum AnimationsCacheMode;
Boolean IsAnimatedVisualLoaded{ get; };
Boolean IsPlaying{ get; };
[MUX_DEFAULT_VALUE("1")]
Expand All @@ -82,6 +111,7 @@ unsealed runtimeclass AnimatedVisualPlayer

// Dependency properties
static Windows.UI.Xaml.DependencyProperty AutoPlayProperty{ get; };
static Windows.UI.Xaml.DependencyProperty AnimationsCacheModeProperty{ get; };
static Windows.UI.Xaml.DependencyProperty DiagnosticsProperty{ get; };
static Windows.UI.Xaml.DependencyProperty DurationProperty{ get; };
static Windows.UI.Xaml.DependencyProperty FallbackContentProperty{ get; };
Expand Down
4 changes: 4 additions & 0 deletions dev/AnimatedVisualPlayer/TestUI/AnimatedVisualPlayerPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@
</StackPanel.Resources>
<TextBlock Text="Actions" Style="{ThemeResource StandardGroupHeader}"/>
<Button x:Name="PlayButton" Click="PlayButton_Click">Play forwards from 0 to 1</Button>
<Button x:Name="PlayHalfButton" Click="PlayHalfButton_Click">Play forwards from 0.0 to 0.5</Button>
<Button x:Name="ToZeroKeyframeAnimationPlayButton" Click="ToZeroKeyframeAnimationPlayButton_Click">Play forwards from 0.35 to 0</Button>
<Button x:Name="AroundTheEndAnimationPlayButton" Click="AroundTheEndAnimationPlayButton_Click">Play forwards from 0.35 to 0.3</Button>
<Button x:Name="FromOneKeyframeAnimationPlayButton" Click="FromOneKeyframeAnimationPlayButton_Click">Play forwards from 1 to 0.35</Button>
<Button x:Name="SetProgressButton" Click="SetProgressButton_Click">Set progress to 0.4</Button>
<Button x:Name="SwitchCacheModeButton" Click="SwitchCacheModeButton_Click">Switch cache mode</Button>
<Button x:Name="StopButton" Click="StopButton_Click">Stop</Button>
<Button x:Name="ReverseNegativePlaybackRateAnimationPlayButton" Click="ReverseNegativePlaybackRateAnimationPlayButton_Click">Play backwards from 1 to 0.5 then forwards from 0.5 to 1</Button>
<Button x:Name="ReversePositivePlaybackRateAnimationPlayButton" Click="ReversePositivePlaybackRateAnimationPlayButton_Click">Play forwards from 0 to 0.5 then backwards from 0.5 to 0</Button>
<Button x:Name="FallenBackButton" Click="FallenBackButton_ClickAsync">Fall back</Button>
Expand Down
35 changes: 33 additions & 2 deletions dev/AnimatedVisualPlayer/TestUI/AnimatedVisualPlayerPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ async void Play(double from, double to, TextBox statusTextBox)
IsPlayingTextBoxBeforePlaying.Text = Player.IsPlaying.ToString();

// Start playing and concurrently get the IsPlaying state.
Task task1 = Player.PlayAsync(0, 1, false).AsTask();
Task task1 = Player.PlayAsync(from, to, false).AsTask();
Task task2 = GetIsPlayingAsync();

// Wait for playing to finish.
Expand All @@ -99,6 +99,13 @@ void PlayButton_Click(object sender, RoutedEventArgs e)
PlayForward(0, 1, ProgressTextBox);
}

// Play from 0 to 1.
void PlayHalfButton_Click(object sender, RoutedEventArgs e)
{
IsPlayingTextBoxBeforePlaying.Text = Player.IsPlaying.ToString();
PlayForward(0, 0.5, ProgressTextBox);
}

// Play forwards from 0.35 to 0.
void ToZeroKeyframeAnimationPlayButton_Click(object sender, RoutedEventArgs e)
{
Expand All @@ -117,12 +124,36 @@ void FromOneKeyframeAnimationPlayButton_Click(object sender, RoutedEventArgs e)
PlayForward(1, 0.35, FromOneKeyframeAnimationProgressTextBox);
}

void SetProgressButton_Click(object sender, RoutedEventArgs e)
{
Player.SetProgress(0.4);
}

void SwitchCacheModeButton_Click(object sender, RoutedEventArgs e)
{
if (Player.AnimationsCacheMode == Microsoft.UI.Xaml.Controls.AnimationsCacheModeEnum.Always)
{
Player.AnimationsCacheMode = Microsoft.UI.Xaml.Controls.AnimationsCacheModeEnum.None;
SwitchCacheModeButton.Content = "Switch Cache Mode (Current: None)";
}
else if (Player.AnimationsCacheMode == Microsoft.UI.Xaml.Controls.AnimationsCacheModeEnum.None)
{
Player.AnimationsCacheMode = Microsoft.UI.Xaml.Controls.AnimationsCacheModeEnum.Always;
SwitchCacheModeButton.Content = "Switch Cache Mode (Current: Always)";
}
}

void StopButton_Click(object sender, RoutedEventArgs e)
{
Player.Stop();
}

// Play backwards from 1 to 0.5 then forwards from 0.5 to 1.
async void ReverseNegativePlaybackRateAnimationPlayButton_Click(object sender, RoutedEventArgs e)
{
// Start playing backwards from 1 to 0.
Player.PlaybackRate = -1;
Task task1 = Player.PlayAsync(0, 1, false).AsTask();
Task task1 = Player.PlayAsync(0.0, 1.0, false).AsTask();

// Reverse direction after half of the animation duration.
Task task2 = DelayForHalfAnimationDurationThenReverse();
Expand Down
Loading