在我们的应用中,我们在ChildWindow
中有一些滚动信用。显示此窗口时,我们的CPU利用率非常高。文本使用BitmapCache
并启用了硬件加速。即使从子窗口中删除剪切矩形和阴影后,CPU使用率也会攀升至80-90%。当我启用重绘区域可视化时,我发现只有滚动文本被重绘,所以我不确定为什么CPU会变得疯狂。我尝试为Canvas.Top
的{{1}}和TranslateY
属性设置动画来进行滚动。
关于可能导致这部动画如此昂贵的想法?那里有没有关于优化动画的建议的好文章?这是我的XAML:
CompositeTransform
更新
CPU问题与<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>
本身没有直接关系,而是与Silverlight浪费地重新渲染的ChildWindow
对象无关。我已经添加了一个答案来描述我是如何解决这个问题的。
答案 0 :(得分:2)
在Silverlight中设置动画文本时,您应该在TextBlock上将TextHintingMode附加属性设置为“动画”。为了提高文本可读性,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开始调试性能。有一个good tutorial使用此命令行工具来查看Silverlight应用程序的一部分运行时花费的大部分CPU时间。您应该注意在 agcore.dll,npctrl.dll和coreclr.dll 中花费了多少CPU时间。如果您的性能问题与重绘有关,那么大部分CPU时间可能花费在agcore.dll上,因为它可以完成Silverlight的大多数图形相关工作。然后,您可以深入了解它并查看agcore.dll中的特定函数,这些函数在您的采样时间内最常被调用。这通常可以帮助您了解代码的哪些部分导致性能损失以及如何优化。
答案 1 :(得分:2)
事实证明,我们ChildWindow
的内容并不是导致高CPU使用率的内容。相反,DropShadowEffect
后面的许多ChildWindow
个对象正在削弱我们的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的回答,因为它回答了我提出的问题。我已经发布了这个答案,以帮助其他可能遇到效果问题的人。
答案 2 :(得分:0)
您可能将缓存模式设置为BitmapCache有点过于激进。在某些情况下,使用BitmapCache会损害性能。
您可以在http://msdn.microsoft.com/en-us/library/cc189071(VS.95).aspx找到有关Silverlight性能问题的一些基本指导(其中包括使用BitmapCache的一些提示)。
答案 3 :(得分:0)
另一种解决方案可能是重新构建应用了投影效果的控件。假如你需要一个带有阴影效果的边框,你可以用网格包裹它并创建另一个位于第一个边框后面的边框。这样,投影元素没有任何子元素,因此不会重绘。