[英]Silverlight scrolling animation utilizes large amounts of CPU time
在我们的应用程序中,我们在ChildWindow
有一些滚动字幕。 显示此窗口时,我们的CPU使用率很高。 文本正在使用BitmapCache
并且启用了硬件加速。 即使从子窗口中删除了剪切矩形和阴影后,CPU使用率仍攀升至80-90%。 启用重绘区域可视化后,我看到只有滚动文本才被重绘,因此我不确定为什么CPU会发疯。 我尝试为Canvas.Top
和CompositeTransform
的TranslateY
属性设置动画以进行滚动。
关于什么可能导致此动画如此昂贵的任何想法? 是否有任何好的文章提出了总体上优化动画的建议? 这是我的XAML:
<c:ChildWindow xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="OurNamespace.UI.Views.AboutWindow"
Title="About Our App" Width="575"
Height="330" Style="{StaticResource ChromelessChildWindowStyle}"
mc:Ignorable="d"
MouseRightButtonDown="ChildWindow_MouseRightButtonDown"
Background="Black">
<Grid x:Name="LayoutRoot" CacheMode="BitmapCache">
<Grid.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<BeginStoryboard>
<Storyboard Storyboard.TargetName="CreditsTransform"
Storyboard.TargetProperty="TranslateY">
<DoubleAnimation To="-750" RepeatBehavior="Forever"
Duration="0:0:30"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Image HorizontalAlignment="Left" VerticalAlignment="Top"
Source="/Assets/Graphics/SplashAbout/OurBackground.png"/>
<Grid Height="150" Width="570" HorizontalAlignment="Right"
Margin="0,0,0,80" VerticalAlignment="Bottom">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock x:Name="AppVersionTextBlock" Margin="10,0"
VerticalAlignment="Center" FontFamily="Arial"
FontSize="12" Foreground="White"
Text="{Binding VersionInfo, FallbackValue=Version 2.0.0}"
TextWrapping="Wrap"/>
<TextBlock x:Name="FirmwareVersionTextBlock" Margin="10,0"
VerticalAlignment="Center" FontFamily="Arial" FontSize="12"
Foreground="White" Text="{Binding FirmwareVersion.Value, FallbackValue=Firmware Version 1.0.0}"
TextWrapping="Wrap"
Visibility="{Binding FirmwareVersionVisibility.Value}"
TextAlignment="Right"/>
<Canvas Margin="0" Grid.Row="1" x:Name="Viewport">
<Canvas.Clip>
<RectangleGeometry Rect="0,0,575,120"/>
</Canvas.Clip>
<TextBlock FontFamily="Arial" FontSize="12" Width="555"
Foreground="White" TextWrapping="Wrap" Canvas.Left="10"
Text="{Binding Credits}" x:Name="Credits"
TextAlignment="Center" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<CompositeTransform TranslateY="0" x:Name="CreditsTransform"/>
</TextBlock.RenderTransform>
<TextBlock.CacheMode>
<BitmapCache/>
</TextBlock.CacheMode>
</TextBlock>
</Canvas>
</Grid>
<TextBlock Foreground="White" Text="{Binding CopyrightInfo, FallbackValue=© 2010 Our Company}"
TextWrapping="Wrap" Width="413" FontSize="10"
FontFamily="Arial" Height="44" HorizontalAlignment="Right"
Margin="0,0,30,21" VerticalAlignment="Bottom"/>
<Button x:Name="CancelButton" Width="575" Height="330" Opacity="0"
Click="CancelButton_Click" HorizontalAlignment="Right"
Margin="0" VerticalAlignment="Bottom"/>
</Grid>
</c:ChildWindow>
更新:
CPU问题与ChildWindow
本身没有直接关系,而是与Silverlight无用地重新渲染其下的DropShadowEffect
对象有关。 我添加了一个答案来描述我如何解决此问题。
在Silverlight中设置文本动画时,应在TextBlock 上将TextHintingMode的附加属性设置为“ Animated”。 为了提高文本的可读性,Silverlight通常使用提示来平滑每个文本字形。 在为文本设置动画时,这可能会对性能产生很大的影响,因为更改将导致重新计算字形的最清晰性,动画中每秒最多可能发生60帧。
<TextBlock TextOptions.TextHintingMode="Animated"
FontFamily="Arial" FontSize="12" Width="555"
Foreground="White" TextWrapping="Wrap" Canvas.Left="10"
Text="{Binding Credits}" x:Name="Credits"
TextAlignment="Center" RenderTransformOrigin="0.5,0.5">
...
</TextBlock>
如果那不能解决您的问题,建议您使用XPerf开始调试性能。 有一个很好的教程,介绍如何使用此命令行工具查看部分Silverlight应用程序运行时花了大部分CPU时间。 您应该注意agcore.dll,npctrl.dll和coreclr.dll中花费了多少CPU时间。 如果您的性能问题与重绘有关,则大多数CPU时间可能都在agcore.dll中花费了,因为这与Silverlight的大部分图形工作有关。 然后,您可以对其进行深入研究,并查看agcore.dll中的特定函数,这些函数在您的采样时间内被最频繁地调用。 这通常可以帮助您认识到代码的哪些部分导致性能下降以及如何进行优化。
事实证明,并不是导致ChildWindow
高使用率的是ChildWindow
的内容。 相反, ChildWindow
后面的许多DropShadowEffect
对象正在消耗我们的CPU。 显然,对于效果的重画逻辑,Silverlight确实很傻。
最终,我们将逐步淘汰效果的使用,这实在令人遗憾。 但是,由于这需要进行大量工作,因此在此期间,我创建了一个方便的附加属性和实用程序方法来临时禁用效果并重新启用它们:
private static IDictionary<UIElement, Effect> _effects =
new Dictionary<UIElement, Effect>();
public static readonly DependencyProperty CanDisableEffectsProperty = DependencyProperty.RegisterAttached(
"CanDisableEffects", typeof(bool), typeof(FrameworkUtils),
new PropertyMetadata(onCanDisableEffectsChanged));
public static bool GetCanDisableEffects(DependencyObject obj)
{
return (bool)obj.GetValue(CanDisableEffectsProperty);
}
public static void SetCanDisableEffects(
DependencyObject obj, bool value)
{
obj.SetValue(CanDisableEffectsProperty, value);
}
private static void onCanDisableEffectsChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var enable = (bool)args.NewValue;
var uiElement = obj as UIElement;
var fElement = obj as FrameworkElement;
if (uiElement != null)
{
if (enable && uiElement.Effect != null)
{
_effects[uiElement] = uiElement.Effect;
}
}
if (fElement != null)
{
Action applyToChildren = () => uiElement.GetVisualChildren()
.ForEach(c => SetCanDisableEffects(c, enable));
applyToChildren();
fElement.Loaded += (s, e) => applyToChildren();
}
}
public static void DisableAllEffects()
{
_effects.Keys.ForEach(ui => ui.Effect = null);
}
public static void EnableAllEffects()
{
_effects.ForEach(p => p.Key.Effect = p.Value);
}
所以我要做的是将CanDisableEffects
属性附加到所有包含效果的项目。 然后,当加载带有动画的子窗口时,我将调用DisableAllEffects
方法。 然后,当ChildWindow.Closed
事件触发时,我调用EnableAllEffects
重新启用。 由于ChildWindow
的叠加层仍然会使背景变暗,因此效果的消除并不明显,但是减少了CPU使用率。
我接受Dan Auclair的回答,因为它可以按要求回答我的问题。 我已发布此答案,以帮助可能遇到效果问题的其他任何人。
您可能过于积极地将缓存模式设置为BitmapCache。 在某些情况下,使用BitmapCache可能会影响性能。
您可以在http://msdn.microsoft.com/zh-cn/library/cc189071(VS.95).aspx中找到有关Silverlight性能问题的一些基本指南(其中包括一些有关使用BitmapCache的技巧)。
另一个解决方案可能是重组应用了阴影效果的控件。 假设需要边框来产生阴影效果,则可以用网格将其包裹起来,并在第一个边框后面创建另一个边框。 这样,阴影元素没有任何子元素,因此不会被重绘。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.