2

在我们的应用程序中,我们在ChildWindow. 显示此窗口时,我们的 CPU 利用率非常高。文本使用的是 aBitmapCache并且启用了硬件加速。即使在从子窗口中删除剪切矩形和投影后,CPU 使用率也会攀升至 80-90%。当我启用重绘区域可视化时,我看到只有滚动文本被重绘,所以我不确定为什么 CPU 会发疯。Canvas.Top我尝试对两者和 a 的TranslateY属性进行动画处理CompositeTransform以进行滚动。

关于什么可能导致这个动画如此昂贵的任何想法?有没有什么好的文章可以提供一般优化动画的建议?这是我的 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本身没有直接关系,而是与DropShadowEffectSilverlight 浪费性地重新渲染的对象有关。我添加了一个答案来描述我如何解决这个问题。

4

4 回答 4

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开始调试性能。有一个很好的教程介绍了如何使用这个命令行工具来查看当您的 Silverlight 应用程序的一部分运行时您的大部分 CPU 时间都花在了哪里。您应该注意在agcore.dll、npctrl.dll 和 coreclr.dll中花费了多少 CPU 时间. 如果您的性能问题与重绘有关,那么大部分 CPU 时间可能都花在了 agcore.dll 中,因为它完成了 Silverlight 的大部分图形相关工作。然后,您可以深入研究并查看在您的采样期间最常调用的 agcore.dll 中的特定函数。这通常可以帮助您了解代码的哪些部分导致性能下降以及如何优化。

于 2010-08-17T15:42:41.847 回答
2

ChildWindow事实证明,导致 CPU 使用率高的并不是我们的内容。相反,背后的许多DropShadowEffect对象正在消耗我们的 CPU。显然,Silverlight 在其效果的重绘逻辑方面确实很愚蠢。ChildWindow

最终,我们将逐步淘汰效果的使用,这真的很可悲。但由于工作量很大,在此期间,我创建了一个方便的附加属性和实用方法,用于暂时禁用效果并重新启用它们:

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 的回答,因为它回答了我的问题。我已经发布了这个答案来帮助其他可能遇到效果问题的人。

于 2010-08-19T20:39:24.387 回答
0

您可能过于激进地将缓存模式设置为 BitmapCache。在某些情况下,使用 BitmapCache 会损害性能。

您可以在http://msdn.microsoft.com/en-us/library/cc189071(VS.95).aspx找到有关 Silverlight 性能问题的一些基本指南(其中包括一些有关使用 BitmapCache 的提示)。

于 2010-08-17T01:58:14.663 回答
0

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

于 2011-03-26T12:16:07.640 回答