Silverlight滚动动画利用大量CPU时间

时间:2010-08-17 01:39:32

标签: silverlight optimization silverlight-4.0 effects storyboard

在我们的应用中,我们在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对象无关。我已经添加了一个答案来描述我是如何解决这个问题的。

4 个答案:

答案 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)

另一种解决方案可能是重新构建应用了投影效果的控件。假如你需要一个带有阴影效果的边框,你可以用网格包裹它并创建另一个位于第一个边框后面的边框。这样,投影元素没有任何子元素,因此不会重绘。