From 9eb8b5c6686a160a0707f70ca78b7ff4f6852b4b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 10 Aug 2017 14:36:39 +0200 Subject: [PATCH] Memory and performance improvements to the XAML acrylic brush --- .../Behaviours/Effects/AcrylicEffectMode.cs | 9 +- .../Cache/HostBackdropInstanceWrapper.cs | 35 ++++++ .../Brushes/CustomAcrylicBrush.cs | 107 ++++++++++++++---- .../Properties/AssemblyInfo.cs | 2 +- .../UICompositionAnimations.csproj | 1 + .../UICompositionAnimations.nuget.props | 2 +- .../UICompositionAnimations.nuget.targets | 2 +- .../UICompositionAnimations.nuspec | 6 +- UICompositionAnimations/project.json | 4 +- 9 files changed, 138 insertions(+), 30 deletions(-) create mode 100644 UICompositionAnimations/Brushes/Cache/HostBackdropInstanceWrapper.cs diff --git a/UICompositionAnimations/Behaviours/Effects/AcrylicEffectMode.cs b/UICompositionAnimations/Behaviours/Effects/AcrylicEffectMode.cs index e302f3e..d7f3b7d 100644 --- a/UICompositionAnimations/Behaviours/Effects/AcrylicEffectMode.cs +++ b/UICompositionAnimations/Behaviours/Effects/AcrylicEffectMode.cs @@ -1,18 +1,21 @@ -namespace UICompositionAnimations.Behaviours.Effects +using System; + +namespace UICompositionAnimations.Behaviours.Effects { /// /// Indicates the UI mode for an acrylic effect brush /// + [Flags] public enum AcrylicEffectMode { /// /// The source content is the blurred UI of the application window /// - InAppBlur, + InAppBlur = 1, /// /// The source content is the host screen /// - HostBackdrop + HostBackdrop = 1 << 1 } } \ No newline at end of file diff --git a/UICompositionAnimations/Brushes/Cache/HostBackdropInstanceWrapper.cs b/UICompositionAnimations/Brushes/Cache/HostBackdropInstanceWrapper.cs new file mode 100644 index 0000000..b776414 --- /dev/null +++ b/UICompositionAnimations/Brushes/Cache/HostBackdropInstanceWrapper.cs @@ -0,0 +1,35 @@ +using Windows.Graphics.Effects; +using Windows.UI.Composition; +using JetBrains.Annotations; + +namespace UICompositionAnimations.Brushes.Cache +{ + /// + /// A simple class that holds information on a instance and its effects pipeline + /// + internal sealed class HostBackdropInstanceWrapper + { + /// + /// Gets the partial pipeline with the host backdrop effect + /// + [NotNull] + public IGraphicsEffectSource Pipeline { get; } + + /// + /// Gets the host backdrop effect brush instance + /// + [NotNull] + public CompositionBackdropBrush Brush { get; } + + /// + /// Creates a new wrapper instance with the given parameters + /// + /// The current effects pipeline + /// The host backdrop brush instance + public HostBackdropInstanceWrapper([NotNull] IGraphicsEffectSource pipeline, [NotNull] CompositionBackdropBrush brush) + { + Pipeline = pipeline; + Brush = brush; + } + } +} diff --git a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs index 73adbaa..0b5f1fd 100644 --- a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs +++ b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs @@ -8,8 +8,10 @@ using Windows.UI.Composition; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; +using JetBrains.Annotations; using UICompositionAnimations.Behaviours; using UICompositionAnimations.Behaviours.Effects; +using UICompositionAnimations.Brushes.Cache; using UICompositionAnimations.Enums; using UICompositionAnimations.Helpers; @@ -254,6 +256,51 @@ protected override async void OnDisconnected() base.OnDisconnected(); } + #region CompositionBackdropBrush cache + + // The synchronization semaphore for the in-app backdrop brush + private static readonly SemaphoreSlim BackdropSemaphore = new SemaphoreSlim(1); + + // The cached in-app backdrop brush + private static CompositionBackdropBrush _BackdropInstance; + + // The name to use for the in-app backdrop reference parameter + private const String BackdropReferenceParameterName = "BackdropBrush"; + + // The synchronization semaphore for the host backdrop brush + private static readonly SemaphoreSlim HostBackdropSemaphore = new SemaphoreSlim(1); + + // The cached host backdrop effect and partial pipeline to reuse + private static HostBackdropInstanceWrapper _HostBackdropCache; + + // The name to use for the host backdrop reference parameter + private const String HostBackdropReferenceParameterName = "HostBackdropBrush"; + + /// + /// Clears the internal cache of instances + /// + [PublicAPI] + public static async Task ClearCacheAsync(AcrylicEffectMode targets) + { + // In-app backdrop brush + if (targets.HasFlag(AcrylicEffectMode.InAppBlur)) + { + await BackdropSemaphore.WaitAsync(); + _BackdropInstance = null; + BackdropSemaphore.Release(); + } + + // Host backdrop brush + if (targets.HasFlag(AcrylicEffectMode.HostBackdrop)) + { + await HostBackdropSemaphore.WaitAsync(); + _HostBackdropCache = null; + HostBackdropSemaphore.Release(); + } + } + + #endregion + /// /// Initializes the appropriate acrylic effect for the current instance /// @@ -291,39 +338,61 @@ private async Task SetupEffectAsync() IGraphicsEffectSource baseEffect; if (Mode == AcrylicEffectMode.InAppBlur) { - // Prepare a luminosity to alpha effect to adjust the background contrast - CompositionBackdropBrush backdropBrush = Window.Current.Compositor.CreateBackdropBrush(); + // Manage the cache + await BackdropSemaphore.WaitAsync(); + if (_BackdropInstance == null) + { + _BackdropInstance = Window.Current.Compositor.CreateBackdropBrush(); + } + + // Prepare the blur effect for the backdrop brush baseEffect = new GaussianBlurEffect { Name = "Blur", BlurAmount = 0f, // The blur value is inserted later on as it isn't applied correctly when set from here BorderMode = EffectBorderMode.Hard, Optimization = EffectOptimization.Balanced, - Source = new CompositionEffectSourceParameter(nameof(backdropBrush)) + Source = new CompositionEffectSourceParameter(nameof(BackdropReferenceParameterName)) }; animatableParameters.Add(BlurAmountParameterName); - sourceParameters.Add(nameof(backdropBrush), backdropBrush); + sourceParameters.Add(nameof(BackdropReferenceParameterName), _BackdropInstance); + BackdropSemaphore.Release(); } else { - // Prepare a luminosity to alpha effect to adjust the background contrast - CompositionBackdropBrush hostBackdropBrush = Window.Current.Compositor.CreateHostBackdropBrush(); - CompositionEffectSourceParameter backgroundParameter = new CompositionEffectSourceParameter(nameof(hostBackdropBrush)); - LuminanceToAlphaEffect alphaEffect = new LuminanceToAlphaEffect { Source = backgroundParameter }; - OpacityEffect opacityEffect = new OpacityEffect + // Manage the cache + await HostBackdropSemaphore.WaitAsync(); + if (_HostBackdropCache == null) { - Source = alphaEffect, - Opacity = 0.4f // Reduce the amount of the effect to avoid making bright areas completely black - }; + // Prepare a luminosity to alpha effect to adjust the background contrast + CompositionBackdropBrush hostBackdropBrush = Window.Current.Compositor.CreateHostBackdropBrush(); + CompositionEffectSourceParameter backgroundParameter = new CompositionEffectSourceParameter(nameof(hostBackdropBrush)); + LuminanceToAlphaEffect alphaEffect = new LuminanceToAlphaEffect { Source = backgroundParameter }; + OpacityEffect opacityEffect = new OpacityEffect + { + Source = alphaEffect, + Opacity = 0.4f // Reduce the amount of the effect to avoid making bright areas completely black + }; + + // Layer [0,1,3] - Desktop background with blur and opacity mask + baseEffect = new BlendEffect + { + Background = backgroundParameter, + Foreground = opacityEffect, + Mode = BlendEffectMode.Multiply + }; + sourceParameters.Add(HostBackdropReferenceParameterName, hostBackdropBrush); - // Layer [0,1,3] - Desktop background with blur and opacity mask - baseEffect = new BlendEffect + // Update the cache + _HostBackdropCache = new HostBackdropInstanceWrapper(baseEffect, hostBackdropBrush); + } + else { - Background = backgroundParameter, - Foreground = opacityEffect, - Mode = BlendEffectMode.Multiply - }; - sourceParameters.Add(nameof(hostBackdropBrush), hostBackdropBrush); + // Reuse the cached pipeline and effect + baseEffect = _HostBackdropCache.Pipeline; + sourceParameters.Add(HostBackdropReferenceParameterName, _HostBackdropCache.Brush); + } + HostBackdropSemaphore.Release(); } // Get the noise brush using Win2D diff --git a/UICompositionAnimations/Properties/AssemblyInfo.cs b/UICompositionAnimations/Properties/AssemblyInfo.cs index ff17c53..7ed62e1 100644 --- a/UICompositionAnimations/Properties/AssemblyInfo.cs +++ b/UICompositionAnimations/Properties/AssemblyInfo.cs @@ -23,6 +23,6 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.10.1.0")] +[assembly: AssemblyVersion("2.10.2.0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: ComVisible(false)] \ No newline at end of file diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 3c0f9a6..768de54 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -122,6 +122,7 @@ + diff --git a/UICompositionAnimations/UICompositionAnimations.nuget.props b/UICompositionAnimations/UICompositionAnimations.nuget.props index f8463e5..e6049f0 100644 --- a/UICompositionAnimations/UICompositionAnimations.nuget.props +++ b/UICompositionAnimations/UICompositionAnimations.nuget.props @@ -16,6 +16,6 @@ - + \ No newline at end of file diff --git a/UICompositionAnimations/UICompositionAnimations.nuget.targets b/UICompositionAnimations/UICompositionAnimations.nuget.targets index 8eed0aa..3c7c0a3 100644 --- a/UICompositionAnimations/UICompositionAnimations.nuget.targets +++ b/UICompositionAnimations/UICompositionAnimations.nuget.targets @@ -7,6 +7,6 @@ - + \ No newline at end of file diff --git a/UICompositionAnimations/UICompositionAnimations.nuspec b/UICompositionAnimations/UICompositionAnimations.nuspec index 932d921..7fb8039 100644 --- a/UICompositionAnimations/UICompositionAnimations.nuspec +++ b/UICompositionAnimations/UICompositionAnimations.nuspec @@ -1,15 +1,15 @@ - + Sergio0694.UWP.UICompositionAnimations - 2.10.1.0 + 2.10.2.0 UICompositionAnimations A wrapper UWP PCL to work with Windows.UI.Composition and XAML animations, and Win2D effects Sergio Pedri Sergio Pedri https://github.com/Sergio0694/UICompositionAnimations false - Minor XAML designer fixes in the LightsSourceHelper class + Minor memory and performance improvements in the custom acrylic brush Copyright 2017 uwp composition animations xaml csharp windows winrt universal app ui win2d graphics diff --git a/UICompositionAnimations/project.json b/UICompositionAnimations/project.json index d09c69a..61413c9 100644 --- a/UICompositionAnimations/project.json +++ b/UICompositionAnimations/project.json @@ -1,7 +1,7 @@ { "dependencies": { - "JetBrains.Annotations": "10.4.0", - "Microsoft.NETCore.UniversalWindowsPlatform": "5.3.3", + "JetBrains.Annotations": "11.0.0", + "Microsoft.NETCore.UniversalWindowsPlatform": "5.3.4", "System.Threading.Tasks.Extensions": "4.3.0", "Win2D.uwp": "1.21.0" },